diff --git a/.gitignore b/.gitignore index a4beeeac..2cb4fd36 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ target/ *.iml **/src/main/resources/**.tgz /nbactions.xml +**node_modules** \ No newline at end of file diff --git a/bundle-original/pom.xml b/bundle-original/pom.xml deleted file mode 100644 index 9b1f1431..00000000 --- a/bundle-original/pom.xml +++ /dev/null @@ -1,87 +0,0 @@ - - - 4.0.0 - - - io.mvnpm - esbuild-java-parent - 999-SNAPSHOT - - esbuild-java-bundle-original - Esbuild wrapper for Java bundled with original binaries - - 0.25.9 - - - - io.mvnpm - esbuild-java - ${project.version} - - - org.junit.jupiter - junit-jupiter-engine - test - - - org.hamcrest - hamcrest-all - 1.3 - test - - - - - - src/main/resources - false - - - src/main/resources-filtered - true - - - - - net.revelc.code.formatter - formatter-maven-plugin - - - net.revelc.code - impsort-maven-plugin - - - org.apache.maven.plugins - maven-antrun-plugin - ${maven-antrun-plugin.version} - - - download - generate-resources - - - - - - - - - - - - - - - - - - - run - - - - - - - - diff --git a/bundle-original/src/main/resources-filtered/esbuild-java-version.properties b/bundle-original/src/main/resources-filtered/esbuild-java-version.properties deleted file mode 100644 index 1254bac2..00000000 --- a/bundle-original/src/main/resources-filtered/esbuild-java-version.properties +++ /dev/null @@ -1 +0,0 @@ -esbuild.version=${esbuild.version} \ No newline at end of file diff --git a/bundle-original/src/test/java/io/mvnpm/esbuild/resolve/BundleResolverTest.java b/bundle-original/src/test/java/io/mvnpm/esbuild/resolve/BundleResolverTest.java deleted file mode 100644 index 04b4335c..00000000 --- a/bundle-original/src/test/java/io/mvnpm/esbuild/resolve/BundleResolverTest.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.mvnpm.esbuild.resolve; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.IOException; -import java.nio.file.Path; - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import io.mvnpm.esbuild.Bundler; - -public class BundleResolverTest extends BundleTester { - - public static final Resolver THROWING_RESOLVER = version -> { - throw new RuntimeException("Should not call this"); - }; - - @BeforeAll - public static void cleanUp() throws IOException { - cleanUpDefault(); - } - - @Test - public void resolve() throws IOException { - // when - final Path resolve = new BundledResolver(THROWING_RESOLVER).resolve(Bundler.ESBUILD_EMBEDDED_VERSION); - - assertFalse(Bundler.ESBUILD_EMBEDDED_VERSION.contains("mvnpm")); - - // then - assertTrue(resolve.toFile().exists()); - } -} diff --git a/bundle-scss/pom.xml b/bundle-scss/pom.xml deleted file mode 100644 index b8a4c625..00000000 --- a/bundle-scss/pom.xml +++ /dev/null @@ -1,86 +0,0 @@ - - - 4.0.0 - - - io.mvnpm - esbuild-java-parent - 999-SNAPSHOT - - esbuild-java-bundle-scss - Esbuild wrapper for Java bundled with mvnpm scss binaries - jar - - - io.mvnpm - esbuild-java - ${project.version} - - - org.junit.jupiter - junit-jupiter-engine - test - - - org.hamcrest - hamcrest-all - 1.3 - test - - - - - - src/main/resources - false - - - src/main/resources-filtered - true - - - - - net.revelc.code.formatter - formatter-maven-plugin - - - net.revelc.code - impsort-maven-plugin - - - org.apache.maven.plugins - maven-antrun-plugin - ${maven-antrun-plugin.version} - - - download - generate-resources - - - - - - - - - - - - - - - - - - - - run - - - - - - - - diff --git a/bundle-scss/src/main/resources-filtered/esbuild-java-version.properties b/bundle-scss/src/main/resources-filtered/esbuild-java-version.properties deleted file mode 100644 index 2780172a..00000000 --- a/bundle-scss/src/main/resources-filtered/esbuild-java-version.properties +++ /dev/null @@ -1,2 +0,0 @@ -esbuild.mvnpm-build.version=${esbuild.mvnpm-build.version} -esbuild.version=${esbuild.version} \ No newline at end of file diff --git a/bundle-scss/src/test/java/io/mvnpm/esbuild/resolve/BundleResolverTest.java b/bundle-scss/src/test/java/io/mvnpm/esbuild/resolve/BundleResolverTest.java deleted file mode 100644 index f76ff8ea..00000000 --- a/bundle-scss/src/test/java/io/mvnpm/esbuild/resolve/BundleResolverTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.mvnpm.esbuild.resolve; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.IOException; -import java.nio.file.Path; - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import io.mvnpm.esbuild.Bundler; - -public class BundleResolverTest extends BundleTester { - - public static final Resolver THROWING_RESOLVER = version -> { - throw new RuntimeException("Should not call this"); - }; - - @BeforeAll - public static void cleanUp() throws IOException { - cleanUpDefault(); - } - - @Test - public void resolve() throws IOException { - // when - final Path resolve = new BundledResolver(THROWING_RESOLVER).resolve(Bundler.ESBUILD_EMBEDDED_VERSION); - assertTrue(Bundler.ESBUILD_EMBEDDED_VERSION.contains("mvnpm")); - // then - assertTrue(resolve.toFile().exists()); - } -} diff --git a/bundle-scss/src/test/java/io/mvnpm/esbuild/resolve/BundleTester.java b/bundle-scss/src/test/java/io/mvnpm/esbuild/resolve/BundleTester.java deleted file mode 100644 index 9926effa..00000000 --- a/bundle-scss/src/test/java/io/mvnpm/esbuild/resolve/BundleTester.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.mvnpm.esbuild.resolve; - -import java.io.IOException; - -import io.mvnpm.esbuild.Bundler; -import io.mvnpm.esbuild.util.PathUtils; - -public abstract class BundleTester { - - public static void cleanUp(String version) throws IOException { - PathUtils.deleteRecursive(Resolvers.getLocation(version)); - } - - public static void cleanUpDefault() throws IOException { - cleanUp(Bundler.ESBUILD_EMBEDDED_VERSION); - } -} diff --git a/core/pom.xml b/core/pom.xml index b8131b65..fc7c8f51 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -1,25 +1,39 @@ 4.0.0 - io.mvnpm esbuild-java-parent 999-SNAPSHOT esbuild-java - Esbuild wrapper for Java - + Esbuild Java Small wrapper around esbuild to be able to use it in Java - + + 0.25.10 + 5.1.2 + 0.4.2 + 5.0.0 + - - + + com.caoccao.javet + javet + ${javet.version} + + + + org.mvnpm + esbuild + ${esbuild.version} + + io.mvnpm importmap 1.0.10 + org.apache.commons commons-compress @@ -28,27 +42,12 @@ com.fasterxml.jackson.core jackson-databind - - - - org.junit.jupiter - junit-jupiter-engine - test - - - org.hamcrest - hamcrest-all - 1.3 - test - - - src/main/resources - false + true src/main/resources-filtered @@ -79,8 +78,129 @@ net.revelc.code impsort-maven-plugin - - + + + darwin-arm64 + + + aarch64 + mac + + + + + org.mvnpm.at.esbuild + darwin-arm64 + ${esbuild.version} + + + com.caoccao.javet + javet-node-macos-arm64 + ${javet.version} + + + + + darwin-x64 + + + x86_64 + mac + + + + + org.mvnpm.at.esbuild + darwin-x64 + ${esbuild.version} + + + com.caoccao.javet + javet-node-macos-x86_64 + ${javet.version} + + + + + linux-x64 + + + x86_64 + unix + + + + + org.mvnpm.at.esbuild + linux-x64 + ${esbuild.version} + + + com.caoccao.javet + javet-node-linux-x86_64 + ${javet.version} + + + + + linux-arm + + + aarch64 + unix + + + + + org.mvnpm.at.esbuild + linux-arm + ${esbuild.version} + + + com.caoccao.javet + javet-node-linux-arm64 + ${javet.version} + + + + + windows-x64 + + + x86_64 + windows + + + + + org.mvnpm.at.esbuild + win32-x64 + ${esbuild.version} + + + com.caoccao.javet + javet-node-windows-x86_64 + ${javet.version} + + + + + windows-ia32 + + + x86 + windows + + + + + org.mvnpm.at.esbuild + win32-ia32 + ${esbuild.version} + + + + diff --git a/core/src/main/java/io/mvnpm/esbuild/BuildEventListener.java b/core/src/main/java/io/mvnpm/esbuild/BuildEventListener.java deleted file mode 100644 index 0b73fd5b..00000000 --- a/core/src/main/java/io/mvnpm/esbuild/BuildEventListener.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.mvnpm.esbuild; - -import io.mvnpm.esbuild.model.WatchBuildResult; - -public interface BuildEventListener { - - void onBuild(WatchBuildResult result); - -} diff --git a/core/src/main/java/io/mvnpm/esbuild/BundleException.java b/core/src/main/java/io/mvnpm/esbuild/BundleException.java index aabb7071..16eb6ae2 100644 --- a/core/src/main/java/io/mvnpm/esbuild/BundleException.java +++ b/core/src/main/java/io/mvnpm/esbuild/BundleException.java @@ -2,19 +2,35 @@ public class BundleException extends RuntimeException { - private final String output; + private final String details; - public BundleException(String message, String output) { + /** + * Creates a BundleException with a message and optional details. + * + * @param message The main exception message. + * @param details Additional details (can be null or empty). + */ + public BundleException(String message, String details) { super(message); - this.output = output; + this.details = details != null ? details : ""; + } + + /** + * Convenience constructor when there are no extra details. + */ + public BundleException(String message) { + this(message, ""); } @Override public String getMessage() { - return super.getMessage() + ": \n" + output; + if (details.isEmpty()) { + return super.getMessage(); + } + return super.getMessage() + ":\n" + details; } - public String output() { - return output; + public String details() { + return details; } } diff --git a/core/src/main/java/io/mvnpm/esbuild/Bundler.java b/core/src/main/java/io/mvnpm/esbuild/Bundler.java index 9e76cff4..c4cb9061 100644 --- a/core/src/main/java/io/mvnpm/esbuild/Bundler.java +++ b/core/src/main/java/io/mvnpm/esbuild/Bundler.java @@ -1,5 +1,6 @@ package io.mvnpm.esbuild; +import static io.mvnpm.esbuild.script.ScriptRunner.getOutDir; import static io.mvnpm.esbuild.util.PathUtils.deleteRecursive; import static java.util.Objects.requireNonNull; @@ -7,13 +8,15 @@ import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.logging.Logger; +import io.mvnpm.esbuild.install.EsBuildDeps; import io.mvnpm.esbuild.install.WebDepsInstaller; import io.mvnpm.esbuild.model.*; -import io.mvnpm.esbuild.resolve.Resolver; +import io.mvnpm.esbuild.script.ScriptRunner; public class Bundler { private static final Logger logger = Logger.getLogger(Bundler.class.getName()); @@ -47,17 +50,30 @@ public static BundleResult bundle(BundleOptions bundleOptions, boolean install) if (install) { install(workDir, bundleOptions); } - final String out = bundleOptions.esBuildConfig().outdir() != null ? bundleOptions.esBuildConfig().outdir() : "dist"; - final Path dist = workDir.resolve(out); + final Path dist = getOutDir(workDir, bundleOptions.esBuildConfig()); final EsBuildConfig esBuildConfig = prepareForBundling(bundleOptions, workDir, dist, false); - final ExecuteResult executeResult = esBuild(workDir, esBuildConfig); + esBuild(workDir, esBuildConfig); if (!Files.isDirectory(dist)) { - throw new BundleException("Unexpected Error during bundling", executeResult.output()); + throw new BundleException("Unexpected Error during bundling"); } - return new BundleResult(dist, workDir, executeResult); + return new BundleResult(dist, workDir); + } + + public static DevResult dev(BundleOptions bundleOptions, boolean install) + throws IOException { + final Path workDir = getWorkDir(bundleOptions); + if (install) { + install(workDir, bundleOptions); + } + final Path dist = getOutDir(workDir, bundleOptions.esBuildConfig()); + final EsBuildConfig esBuildConfig = prepareForBundling(bundleOptions, workDir, dist, true); + + final DevResult devResult = esBuildDev(workDir, esBuildConfig); + devResult.process().init(); + return devResult; } private static EsBuildConfig prepareForBundling(BundleOptions bundleOptions, Path workDir, Path dist, boolean watch) @@ -78,20 +94,6 @@ private static EsBuildConfig prepareForBundling(BundleOptions bundleOptions, Pat .build(); } - public static Watch watch(BundleOptions bundleOptions, BuildEventListener eventListener, boolean install) - throws IOException { - final Path workDir = getWorkDir(bundleOptions); - if (install) { - install(workDir, bundleOptions); - } - final String out = bundleOptions.esBuildConfig().outdir() != null ? bundleOptions.esBuildConfig().outdir() : "dist"; - final Path dist = workDir.resolve(out); - final EsBuildConfig esBuildConfig = prepareForBundling(bundleOptions, workDir, dist, true); - - final WatchStartResult r = esBuildWatch(workDir, esBuildConfig, eventListener); - return new Watch(r.process(), workDir, dist, r.firstBuildResult()); - } - private static Path getWorkDir(BundleOptions bundleOptions) throws IOException { return bundleOptions.workDir() != null ? bundleOptions.workDir() : Files.createTempDirectory("bundle"); @@ -99,7 +101,9 @@ private static Path getWorkDir(BundleOptions bundleOptions) throws IOException { public static boolean install(Path workDir, BundleOptions bundleOptions) throws IOException { final Path nodeModulesDir = getNodeModulesDir(workDir, bundleOptions); - return WebDepsInstaller.install(nodeModulesDir, bundleOptions.dependencies()); + final List dependencies = new ArrayList<>(bundleOptions.dependencies()); + dependencies.addAll(EsBuildDeps.get().deps()); + return WebDepsInstaller.install(nodeModulesDir, dependencies); } protected static Path getNodeModulesDir(Path workDir, BundleOptions bundleOptions) { @@ -112,21 +116,15 @@ public static void clearDependencies(Path nodeModulesDir) throws IOException { deleteRecursive(nodeModulesDir); } - protected static WatchStartResult esBuildWatch(Path workDir, EsBuildConfig esBuildConfig, BuildEventListener listener) + protected static DevResult esBuildDev(Path workDir, EsBuildConfig esBuildConfig) throws IOException { - final Execute execute = getExecute(workDir, esBuildConfig); - return execute.watch(listener); + final ScriptRunner scriptRunner = new ScriptRunner(workDir, esBuildConfig); + return new DevResult(scriptRunner.dev()); } - protected static ExecuteResult esBuild(Path workDir, EsBuildConfig esBuildConfig) throws IOException { - final Execute execute = getExecute(workDir, esBuildConfig); - return execute.executeAndWait(); + protected static void esBuild(Path workDir, EsBuildConfig esBuildConfig) throws IOException { + final ScriptRunner scriptRunner = new ScriptRunner(workDir, esBuildConfig); + scriptRunner.build(); } - private static Execute getExecute(Path workDir, EsBuildConfig esBuildConfig) throws IOException { - final String version = esBuildConfig.esBuildVersion() != null ? esBuildConfig.esBuildVersion() - : Bundler.ESBUILD_EMBEDDED_VERSION; - final Path esBuildExec = Resolver.create().resolve(version); - return new Execute(workDir, esBuildExec.toFile(), esBuildConfig); - } } diff --git a/core/src/main/java/io/mvnpm/esbuild/Execute.java b/core/src/main/java/io/mvnpm/esbuild/Execute.java deleted file mode 100644 index 31f4c4c2..00000000 --- a/core/src/main/java/io/mvnpm/esbuild/Execute.java +++ /dev/null @@ -1,217 +0,0 @@ -package io.mvnpm.esbuild; - -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.BooleanSupplier; -import java.util.function.Consumer; -import java.util.logging.Level; -import java.util.logging.Logger; - -import io.mvnpm.esbuild.model.EsBuildConfig; -import io.mvnpm.esbuild.model.ExecuteResult; -import io.mvnpm.esbuild.model.WatchBuildResult; -import io.mvnpm.esbuild.model.WatchStartResult; - -public class Execute { - - private static final ExecutorService EXECUTOR_STREAMER = Executors.newSingleThreadExecutor(r -> { - final Thread t = new Thread(r, "Process stdout streamer"); - t.setDaemon(true); - return t; - }); - private static final Logger logger = Logger.getLogger(Execute.class.getName()); - - private final Path workDir; - private final File esBuildExec; - private EsBuildConfig esBuildConfig; - private String[] args; - - public Execute(Path workDir, File esBuildExec, EsBuildConfig esBuildConfig) { - this.workDir = workDir; - this.esBuildExec = esBuildExec; - this.esBuildConfig = esBuildConfig; - } - - public Execute(Path workDir, File esBuildExec, String[] args) { - this.workDir = workDir; - this.esBuildExec = esBuildExec; - this.args = args; - } - - public ExecuteResult executeAndWait() throws IOException { - final Process process = createProcess(getCommand(), Optional.empty()); - try { - final int exitCode = process.waitFor(); - final String content = readStream(process.getInputStream()); - final String errors = readStream(process.getErrorStream()); - if (exitCode != 0) { - throw new BundleException(errors.isEmpty() ? "Unexpected Error during bundling" : errors, content); - } - return new ExecuteResult(content); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new RuntimeException(e); - } - } - - public WatchStartResult watch(BuildEventListener listener) throws IOException { - final Process process = createProcess(getCommand(), Optional.of(listener)); - final ExecutorService executorStreamer = Executors - .newSingleThreadExecutor(r -> new Thread(r, "Esbuild watch stdout streamer")); - final ExecutorService executorBuild = Executors - .newSingleThreadExecutor(r -> new Thread(r, "Esbuild build listeners notify")); - final AtomicReference result = new AtomicReference<>(); - CountDownLatch latch = new CountDownLatch(1); - final WatchStartResult.WatchProcess watchProcess = new WatchStartResult.WatchProcess() { - @Override - public boolean isAlive() { - return process.isAlive(); - } - - @Override - public void close() throws IOException { - process.destroyForcibly(); - executorStreamer.shutdownNow(); - executorBuild.shutdownNow(); - try { - process.waitFor(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - if (latch.getCount() == 1) { - latch.countDown(); - } - } - }; - try { - final InputStream processStream = process.getInputStream(); - executorStreamer.execute(new Streamer(executorBuild, process::isAlive, processStream, (r) -> { - if (latch.getCount() == 1) { - result.set(r); - latch.countDown(); - } else { - listener.onBuild(r); - } - }, r -> { - try { - if (process.isAlive()) { - process.destroyForcibly(); - process.waitFor(); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - if (latch.getCount() == 1) { - result.set(r); - latch.countDown(); - } else if (!r.isSuccess()) { - listener.onBuild(r); - } - })); - latch.await(); - if (!process.isAlive() && !result.get().isSuccess()) { - throw result.get().bundleException(); - } - return new WatchStartResult(result.get(), watchProcess); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - watchProcess.close(); - throw new RuntimeException(e); - } - } - - private String[] getCommand() { - String[] command = args != null ? getCommand(args) : getCommand(esBuildConfig); - if (logger.isLoggable(Level.FINE)) { - logger.log(Level.FINE, "running esbuild with flags: \n > `{0}`", String.join(" ", command)); - } - return command; - } - - private String[] getCommand(EsBuildConfig esBuildConfig) { - String[] params = esBuildConfig.toParams(); - return getCommand(params); - } - - private String[] getCommand(String[] args) { - List argList = new ArrayList<>(args.length + 1); - argList.add(esBuildExec.toString()); - argList.addAll(Arrays.asList(args)); - - return argList.toArray(String[]::new); - } - - public Process createProcess(final String[] command, final Optional listener) throws IOException { - return new ProcessBuilder().redirectErrorStream(listener.isPresent()).directory(workDir.toFile()) - .command(command).start(); - } - - private record Streamer(ExecutorService executorBuild, BooleanSupplier isAlive, InputStream processStream, - BuildEventListener listener, Consumer onExit) implements Runnable { - - @Override - public void run() { - final AtomicBoolean hasError = new AtomicBoolean(); - final StringBuilder outputBuilder = new StringBuilder(); - consumeStream(isAlive, processStream, l -> { - logger.fine(l); - outputBuilder.append("\n").append(l); - if (l.contains("build finished")) { - logger.fine("Build finished!"); - final String output = outputBuilder.toString(); - final boolean error = hasError.getAndSet(false); - outputBuilder.setLength(0); - executorBuild.execute(() -> { - if (!error) { - listener.onBuild(new WatchBuildResult(output)); - } else { - listener.onBuild( - new WatchBuildResult(output, new BundleException("Error during bundling", output))); - } - }); - } else if (l.contains("[ERROR]")) { - hasError.set(true); - } - }); - if (!hasError.get()) { - onExit.accept(new WatchBuildResult(outputBuilder.toString())); - } else { - onExit.accept(new WatchBuildResult(outputBuilder.toString(), - new BundleException("Process exited with error", outputBuilder.toString()))); - } - } - } - - private static String readStream(InputStream stream) { - final StringBuilder s = new StringBuilder(); - consumeStream(() -> true, stream, l -> s.append(l).append("\n")); - return s.toString(); - } - - private static void consumeStream(BooleanSupplier stayAlive, InputStream stream, Consumer newLineConsumer) { - try ( - final InputStreamReader in = new InputStreamReader(stream, StandardCharsets.UTF_8); - final BufferedReader reader = new BufferedReader(in)) { - String line; - while ((line = reader.readLine()) != null) { - newLineConsumer.accept(line); - if (!stayAlive.getAsBoolean()) { - break; - } - } - } catch (IOException e) { - // ignore - } - } - -} diff --git a/core/src/main/java/io/mvnpm/esbuild/Main.java b/core/src/main/java/io/mvnpm/esbuild/Main.java index 78161dc3..94ce14e3 100644 --- a/core/src/main/java/io/mvnpm/esbuild/Main.java +++ b/core/src/main/java/io/mvnpm/esbuild/Main.java @@ -1,33 +1,14 @@ package io.mvnpm.esbuild; import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; +import java.net.URISyntaxException; -import io.mvnpm.esbuild.model.ExecuteResult; -import io.mvnpm.esbuild.resolve.Resolver; +import io.mvnpm.esbuild.install.EsBuildDeps; public class Main { - public static void main(String[] args) throws IOException { - String workingDirectory = System.getProperty("user.dir"); - final List arguments = new ArrayList<>(Arrays.asList(args)); - final Optional esbuildVersionArgument = arguments.stream().filter(a -> a.startsWith("--esbuildVersion")) - .findFirst(); - String esbuildVersion = Bundler.ESBUILD_EMBEDDED_VERSION; - if (esbuildVersionArgument.isPresent()) { - arguments.remove(esbuildVersionArgument.get()); - final String unParsedEsbuildVersion = esbuildVersionArgument.get(); - esbuildVersion = unParsedEsbuildVersion.split("=")[1]; - } - final Path esBuildExec = Resolver.create().resolve(esbuildVersion); - final ExecuteResult executeResult = new Execute(Paths.get(workingDirectory), esBuildExec.toFile(), - arguments.toArray(new String[] {})) - .executeAndWait(); - System.out.println(executeResult.output()); + public static void main(String[] args) throws IOException, URISyntaxException { + + System.out.println(EsBuildDeps.get()); } } diff --git a/core/src/main/java/io/mvnpm/esbuild/Watch.java b/core/src/main/java/io/mvnpm/esbuild/Watch.java deleted file mode 100644 index 81bfd41f..00000000 --- a/core/src/main/java/io/mvnpm/esbuild/Watch.java +++ /dev/null @@ -1,53 +0,0 @@ -package io.mvnpm.esbuild; - -import java.io.Closeable; -import java.io.IOException; -import java.nio.file.Path; -import java.util.List; - -import io.mvnpm.esbuild.model.EntryPoint; -import io.mvnpm.esbuild.model.WatchBuildResult; -import io.mvnpm.esbuild.model.WatchStartResult; - -public class Watch implements Closeable { - - private final WatchStartResult.WatchProcess process; - private final Path workDir; - - private final Path dist; - - private final WatchBuildResult firstBuildResult; - - public Watch(WatchStartResult.WatchProcess process, Path workDir, Path dist, WatchBuildResult firstBuildResult) { - this.process = process; - this.workDir = workDir; - this.dist = dist; - this.firstBuildResult = firstBuildResult; - } - - public void updateEntries(List entries) throws IOException { - entries.forEach(entry -> entry.process(workDir)); - } - - @Override - public void close() throws IOException { - process.close(); - } - - public Path workDir() { - return workDir; - } - - public WatchBuildResult firstBuildResult() { - return firstBuildResult; - } - - public boolean isAlive() { - return process.isAlive(); - } - - public Path dist() { - return dist; - } - -} diff --git a/core/src/main/java/io/mvnpm/esbuild/install/EsBuildDeps.java b/core/src/main/java/io/mvnpm/esbuild/install/EsBuildDeps.java new file mode 100644 index 00000000..2c51572e --- /dev/null +++ b/core/src/main/java/io/mvnpm/esbuild/install/EsBuildDeps.java @@ -0,0 +1,79 @@ +package io.mvnpm.esbuild.install; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import io.mvnpm.esbuild.model.WebDependency; + +public record EsBuildDeps(List deps, List plugins) { + + private static AtomicReference DEPS = new AtomicReference<>(); + private static final Pattern GAV_PATTERN = Pattern.compile(".*/(org/mvnpm(?:/[^/]+)*)/([^/]+)/([^/]+)/\\2-\\3\\.jar$"); + + public static EsBuildDeps get() { + return DEPS.updateAndGet(c -> { + if (c == null) { + c = fromClasspath(); + } + return c; + }); + } + + private static EsBuildDeps fromClasspath() { + String classpath = System.getProperty("java.class.path"); + String[] entries = classpath.split(File.pathSeparator); + final List plugins = new ArrayList<>(); + final List deps = Arrays.stream(entries) + .map(Paths::get) + .map(path -> { + final GAV gav = extractGav(path); + if (gav == null) { + return null; + } + final String gavString = gav.toString(); + final Optional type = WebDependency.WebDependencyType + .resolveType(gavString); + if (type.isEmpty()) { + return null; + } + if (gav.artifactId.matches("^esbuild-[a-z]+-plugin$")) { + plugins.add(gav.artifactId); + } + return WebDependency.of(gavString, path, type.get()); + }) + .filter(Objects::nonNull) // only keep dependencies with recognized type + .collect(Collectors.toList()); + return new EsBuildDeps(deps, plugins); + } + + private static GAV extractGav(Path jarPath) { + String path = jarPath.toString().replace("\\", "/"); // normalize for Windows + Matcher matcher = GAV_PATTERN.matcher(path); + if (!matcher.matches()) { + return null; + } + + String groupId = matcher.group(1).replace("/", "."); + String artifactId = matcher.group(2); + String version = matcher.group(3); + return new GAV(groupId, artifactId, version); + } + + private record GAV(String groupId, String artifactId, String version) { + @Override + public String toString() { + return groupId + ":" + artifactId + ":" + version; + } + } + +} diff --git a/core/src/main/java/io/mvnpm/esbuild/model/BundleResult.java b/core/src/main/java/io/mvnpm/esbuild/model/BundleResult.java index ae2b23c1..032a3b9a 100644 --- a/core/src/main/java/io/mvnpm/esbuild/model/BundleResult.java +++ b/core/src/main/java/io/mvnpm/esbuild/model/BundleResult.java @@ -2,5 +2,5 @@ import java.nio.file.Path; -public record BundleResult(Path dist, Path workDir, ExecuteResult result) { +public record BundleResult(Path dist, Path workDir) { } diff --git a/core/src/main/java/io/mvnpm/esbuild/model/DevResult.java b/core/src/main/java/io/mvnpm/esbuild/model/DevResult.java new file mode 100644 index 00000000..e27b908a --- /dev/null +++ b/core/src/main/java/io/mvnpm/esbuild/model/DevResult.java @@ -0,0 +1,14 @@ +package io.mvnpm.esbuild.model; + +import java.io.Closeable; +import java.io.IOException; + +import io.mvnpm.esbuild.script.DevProcess; + +public record DevResult(DevProcess process) implements Closeable { + + @Override + public void close() throws IOException { + process.close(); + } +} diff --git a/core/src/main/java/io/mvnpm/esbuild/model/EsBuildConfig.java b/core/src/main/java/io/mvnpm/esbuild/model/EsBuildConfig.java index 54d192aa..4c723ccb 100644 --- a/core/src/main/java/io/mvnpm/esbuild/model/EsBuildConfig.java +++ b/core/src/main/java/io/mvnpm/esbuild/model/EsBuildConfig.java @@ -7,20 +7,21 @@ import java.util.Map; import java.util.stream.Collectors; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + public record EsBuildConfig( String esBuildVersion, boolean bundle, - String[] entryPoint, + String[] entryPoints, boolean minify, - boolean version, - Map loader, boolean preserveSymlinks, Target target, - boolean watch, - String outdir, String packages, @@ -58,14 +59,10 @@ public EsBuildConfig(EsBuildConfigBuilder builder) { builder.entryPoint, builder.minify, - builder.version, - builder.loader, builder.preserveSymlinks, builder.target, - builder.watch, - builder.outdir, builder.packages, @@ -97,13 +94,11 @@ public EsBuildConfigBuilder edit() { return new EsBuildConfigBuilder() .esbuildVersion(this.esBuildVersion) .bundle(this.bundle) - .entryPoint(this.entryPoint) + .entryPoint(this.entryPoints) .minify(this.minify) - .version(this.version) .loader(this.loader) .preserveSymlinks(this.preserveSymlinks) .target(this.target) - .watch(this.watch) .outDir(this.outdir) .packages(this.packages) .platform(this.platform) @@ -124,7 +119,12 @@ public EsBuildConfigBuilder edit() { public enum Format { IIFE, CJS, - ESM, + ESM; + + @Override + public String toString() { + return this.name().toLowerCase(); + } } public enum Loader { @@ -154,7 +154,12 @@ public String toString() { public enum Platform { BROWSER, NODE, - NEUTRAL + NEUTRAL; + + @Override + public String toString() { + return this.name().toLowerCase(); + } } public enum Target { @@ -165,7 +170,12 @@ public enum Target { EDGE16, NODE10, IE9, - OPERA45 + OPERA45; + + @Override + public String toString() { + return this.name().toLowerCase(); + } } public String[] toParams() { @@ -182,7 +192,7 @@ public String[] toParams() { ((List) value).forEach(e -> result.add("--%s:%s".formatted(convertField(fieldName), e.toString()))); } else if (value instanceof Map) { result.addAll(mapToString(fieldName, (Map) value)); - } else if ("entryPoint".equals(field.getName())) { + } else if ("entryPoints".equals(field.getName())) { result.addAll(List.of((String[]) value)); } else if (!(value instanceof Boolean)) { String fn = convertField(fieldName); @@ -201,6 +211,53 @@ public String[] toParams() { return result.toArray(String[]::new); } + public String toJson() { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode node = mapper.createObjectNode(); + final Field[] fields = EsBuildConfig.class.getDeclaredFields(); + for (Field field : fields) { + try { + final Object value = field.get(this); + final String fieldName = field.getName(); + + if ("serve".equals(fieldName)) { + continue; + } + if (value instanceof Boolean) { + node.put(fieldName, (Boolean) value); + } else if (value instanceof Map map) { + if (map.isEmpty()) + continue; + + ObjectNode subNode = mapper.createObjectNode(); + map.forEach((key, v) -> subNode.put(key.toString(), v.toString().replaceAll("_", "-"))); + node.set(fieldName, subNode); + } else if (value instanceof List list) { + if (list.isEmpty()) + continue; + ArrayNode arrayNode = mapper.createArrayNode(); + list.forEach(v -> arrayNode.add(v.toString())); + node.set(fieldName, arrayNode); + } else if (value instanceof String[] list) { + if (list.length == 0) + continue; + ArrayNode arrayNode = mapper.createArrayNode(); + Arrays.stream(list).toList().forEach(arrayNode::add); + node.set(fieldName, arrayNode); + } else if (value != null) { + node.put(fieldName, value.toString()); + } + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + try { + return mapper.writeValueAsString(node); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + private static String convertField(String field) { final String[] split = field.split("(?=\\p{Upper})"); return Arrays.stream(split).map(String::toLowerCase).collect(Collectors.joining("-")); diff --git a/core/src/main/java/io/mvnpm/esbuild/model/EsBuildConfigBuilder.java b/core/src/main/java/io/mvnpm/esbuild/model/EsBuildConfigBuilder.java index d04795d2..8055ed72 100644 --- a/core/src/main/java/io/mvnpm/esbuild/model/EsBuildConfigBuilder.java +++ b/core/src/main/java/io/mvnpm/esbuild/model/EsBuildConfigBuilder.java @@ -58,7 +58,7 @@ public class EsBuildConfigBuilder { List external = new ArrayList<>(); - EsBuildConfigBuilder() { + public EsBuildConfigBuilder() { } public EsBuildConfigBuilder withDefault() { diff --git a/core/src/main/java/io/mvnpm/esbuild/model/WatchStartResult.java b/core/src/main/java/io/mvnpm/esbuild/model/WatchStartResult.java deleted file mode 100644 index 4ecf5434..00000000 --- a/core/src/main/java/io/mvnpm/esbuild/model/WatchStartResult.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.mvnpm.esbuild.model; - -import java.io.Closeable; - -public record WatchStartResult(WatchBuildResult firstBuildResult, WatchProcess process) { - - public interface WatchProcess extends Closeable { - - boolean isAlive(); - - } - -} diff --git a/core/src/main/java/io/mvnpm/esbuild/script/BuildScript.java b/core/src/main/java/io/mvnpm/esbuild/script/BuildScript.java new file mode 100644 index 00000000..52476df4 --- /dev/null +++ b/core/src/main/java/io/mvnpm/esbuild/script/BuildScript.java @@ -0,0 +1,120 @@ +package io.mvnpm.esbuild.script; + +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReference; + +import com.caoccao.javet.enums.JSRuntimeType; +import com.caoccao.javet.enums.V8AwaitMode; +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.interop.NodeRuntime; +import com.caoccao.javet.interop.V8Locker; +import com.caoccao.javet.interop.engine.IJavetEngine; +import com.caoccao.javet.interop.engine.IJavetEnginePool; +import com.caoccao.javet.interop.engine.JavetEngineConfig; +import com.caoccao.javet.interop.engine.JavetEnginePool; +import com.caoccao.javet.values.reference.V8ValueGlobalObject; +import com.caoccao.javet.values.reference.V8ValueObject; +import com.caoccao.javet.values.reference.V8ValuePromise; + +import io.mvnpm.esbuild.BundleException; +import io.mvnpm.esbuild.install.EsBuildDeps; +import io.mvnpm.esbuild.model.EsBuildConfig; + +public class BuildScript { + + private static final AtomicReference> POOL_REF = new AtomicReference<>(); + // language=JavaScript + private static final String SCRIPT = """ + const esbuild = require('esbuild'); + const requiredPlugins = []; + for(let plugin of plugins) { + console.debug(`Adding ${plugin}`); + requiredPlugins.push(require(plugin).default.call()); + } + + async function build () { + const {watch, ...options} = JSON.parse(config); + console.log(`Running esbuild (${esbuild.version})`); + try { + await esbuild.build({ + ...options, + logLevel: "warning", + plugins: requiredPlugins + }); + console.log("build finished"); + return {success: true}; + } catch(e) { + console.log("Error during build."); + return {success: false, error: e}; + } + } + """; + + public static void build(Path workDir, EsBuildConfig config) { + try (IJavetEngine javetEngine = getOrCreatePool().getEngine()) { + try (NodeRuntime nodeRuntime = javetEngine.getV8Runtime()) { + nodeRuntime.allowEval(true); + try (V8Locker ignored = nodeRuntime.getV8Locker()) { + try (V8ValueGlobalObject globalObject = nodeRuntime.getGlobalObject()) { + // bind config + globalObject.set("plugins", EsBuildDeps.get().plugins()); + globalObject.set("config", config.toJson()); + globalObject.set("workDir", workDir.toString()); + + if (!globalObject.has("build")) { + nodeRuntime.getExecutor(SCRIPT).setResourceName(workDir.resolve("build.js").toString()) + .executeVoid(); + } + + try (V8ValuePromise promise = nodeRuntime.getGlobalObject().invoke("build")) { + while (!promise.isFulfilled() && !Thread.currentThread().isInterrupted()) { + nodeRuntime.await(V8AwaitMode.RunOnce); + } + try (final V8ValueObject result = promise.getResult()) { + final boolean success = result.getBoolean("success"); + if (!success) { + throw new BundleException("Error while executing Esbuild", result.getString("error")); + } + } + } catch (JavetException e) { + throw new RuntimeException("Error while preparing Esbuild script", e); + } + } + } + } + } catch (JavetException e) { + throw new RuntimeException(e); + } + + } + + private static IJavetEnginePool getOrCreatePool() { + return POOL_REF.updateAndGet(existing -> { + if (existing != null) { + return existing; + } + return createPool(); + }); + } + + private static JavetEnginePool createPool() { + JavetEngineConfig config = new JavetEngineConfig(); + config.setJSRuntimeType(JSRuntimeType.Node); + config.setAllowEval(true); + return new JavetEnginePool<>(config); + } + + static { + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + IJavetEnginePool pool = POOL_REF.getAndSet(null); + if (pool != null) { + try { + pool.close(); + } catch (Exception e) { + // Log or ignore + } + } + })); + } + +} diff --git a/core/src/main/java/io/mvnpm/esbuild/script/DevProcess.java b/core/src/main/java/io/mvnpm/esbuild/script/DevProcess.java new file mode 100644 index 00000000..160f166b --- /dev/null +++ b/core/src/main/java/io/mvnpm/esbuild/script/DevProcess.java @@ -0,0 +1,17 @@ +package io.mvnpm.esbuild.script; + +import java.io.Closeable; +import java.io.IOException; +import java.nio.file.Path; + +public interface DevProcess extends Closeable { + void init(); + + void build() throws IOException; + + Path workDir(); + + Path dist(); + + boolean isAlive(); +} diff --git a/core/src/main/java/io/mvnpm/esbuild/script/DevScript.java b/core/src/main/java/io/mvnpm/esbuild/script/DevScript.java new file mode 100644 index 00000000..2f36db3d --- /dev/null +++ b/core/src/main/java/io/mvnpm/esbuild/script/DevScript.java @@ -0,0 +1,177 @@ +package io.mvnpm.esbuild.script; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReference; + +import com.caoccao.javet.enums.V8AwaitMode; +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.interop.NodeRuntime; +import com.caoccao.javet.interop.V8Host; +import com.caoccao.javet.values.reference.V8ValueGlobalObject; +import com.caoccao.javet.values.reference.V8ValueObject; +import com.caoccao.javet.values.reference.V8ValuePromise; + +import io.mvnpm.esbuild.BundleException; +import io.mvnpm.esbuild.install.EsBuildDeps; +import io.mvnpm.esbuild.model.EsBuildConfig; + +/** + * Calling build is Threadsafe as soon as init has been called before without a risk of race + */ +public class DevScript implements DevProcess { + private final Path workDir; + private final EsBuildConfig config; + private final Path outDir; + private final AtomicReference nodeRuntime = new AtomicReference<>(); + private final AtomicReference globalObject = new AtomicReference<>(); + private final Object lock = new Object(); + + // language=JavaScript + private static final String SCRIPT = """ + + const esbuild = require('esbuild'); + + const requiredPlugins = []; + + for (let plugin of plugins) { + console.debug(`Adding ${plugin}`); + requiredPlugins.push(require(plugin).default.call()); + } + + + let options = JSON.parse(config); + + let context = null; + + + async function build() { + console.log(`Running esbuild (${esbuild.version})`); + try { + console.debug("Esbuild config:" + config); + if (context == null) { + context = await esbuild.context({ + ...options, + logLevel: "warning", + plugins: requiredPlugins + }); + } + let result = await context.rebuild(); + if (result.errors.length > 0) { + console.debug("build finished with errors: " + result.errors); + return {success: false, error: result.errors[0].text}; + } + + return {success: true}; + } catch (e) { + console.debug("build exception", e); + return {success: false, error: e.message}; + } + } + + async function close() { + console.debug('Closing Esbuild Dev.'); + if (context) { + await context.dispose(); + console.log('Esbuild Dev process closed.'); + } + esbuild.stop(); + options = null; + }; + + + process.on('unhandledRejection', (reason, promise) => { + close(); + }); + """; + + public DevScript(Path workDir, EsBuildConfig config) { + this.workDir = workDir; + this.config = config; + final String out = config.outdir() != null ? config.outdir() : "dist"; + this.outDir = workDir.resolve(out); + } + + @Override + public void init() { + if (nodeRuntime.get() != null) { + throw new IllegalStateException("DevScript has already been initialized"); + } + try { + nodeRuntime.set(V8Host.getNodeInstance().createV8Runtime()); + nodeRuntime.get().allowEval(true); + globalObject.set(nodeRuntime.get().getGlobalObject()); + globalObject.get().set("plugins", EsBuildDeps.get().plugins()); + globalObject.get().set("config", config.toJson()); + globalObject.get().set("workDir", workDir.toString()); + nodeRuntime.get().getExecutor(SCRIPT).setResourceName(workDir.resolve("build.js").toString()).executeVoid(); + } catch (JavetException e) { + close(); + throw new RuntimeException(e); + } + } + + @Override + public void build() throws IOException { + if (nodeRuntime.get() == null) { + throw new IllegalStateException("DevScript has not been initialized"); + } + invokeBuild(); + } + + private void invokeBuild() { + try (V8ValuePromise promise = globalObject.get().invoke("build")) { + while (!promise.isFulfilled() && !Thread.currentThread().isInterrupted()) { + nodeRuntime.get().await(V8AwaitMode.RunOnce); + } + try (final V8ValueObject result = promise.getResult()) { + final boolean success = result.getBoolean("success"); + if (!success) { + throw new BundleException("Error while executing Esbuild", result.getString("error")); + } + } + + } catch (JavetException e) { + throw new RuntimeException(e); + } + + } + + @Override + public Path workDir() { + return workDir; + } + + @Override + public Path dist() { + return outDir; + } + + @Override + public boolean isAlive() { + return nodeRuntime.get() != null && !nodeRuntime.get().isClosed(); + } + + @Override + public void close() { + final NodeRuntime runtime = this.nodeRuntime.getAndSet(null); + final V8ValueGlobalObject obj = this.globalObject.getAndSet(null); + if (runtime != null) { + try { + if (obj.has("close")) { + try (final V8ValuePromise promise = obj.invoke("close")) { + while (!promise.isFulfilled() && !Thread.currentThread().isInterrupted()) { + runtime.await(V8AwaitMode.RunOnce); + } + } + } + runtime.lowMemoryNotification(); + runtime.close(); + } catch (JavetException e) { + throw new RuntimeException(e); + } + } + + } + +} diff --git a/core/src/main/java/io/mvnpm/esbuild/script/ScriptRunner.java b/core/src/main/java/io/mvnpm/esbuild/script/ScriptRunner.java new file mode 100644 index 00000000..0944202d --- /dev/null +++ b/core/src/main/java/io/mvnpm/esbuild/script/ScriptRunner.java @@ -0,0 +1,36 @@ +package io.mvnpm.esbuild.script; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.logging.Logger; + +import io.mvnpm.esbuild.model.EsBuildConfig; + +public class ScriptRunner { + + private static final Logger logger = Logger.getLogger(ScriptRunner.class.getName()); + + private final Path workDir; + private final EsBuildConfig esBuildConfig; + + public ScriptRunner(Path workDir, EsBuildConfig esBuildConfig) { + this.workDir = workDir; + this.esBuildConfig = esBuildConfig; + } + + public static Path getOutDir(Path workDir, EsBuildConfig config) { + final String out = config.outdir() != null ? config.outdir() : "dist"; + return workDir.resolve(out); + } + + public void build() throws IOException { + //WebDepsInstaller.install(workDir.resolve("node_modules"), getDependencies()); + BuildScript.build(workDir, esBuildConfig); + } + + public DevProcess dev() throws IOException { + //WebDepsInstaller.install(workDir.resolve("node_modules"), getDependencies()); + return new DevScript(workDir, esBuildConfig); + } + +} diff --git a/core/src/main/java/io/mvnpm/esbuild/util/Archives.java b/core/src/main/java/io/mvnpm/esbuild/util/Archives.java index 28919be7..3372fa31 100644 --- a/core/src/main/java/io/mvnpm/esbuild/util/Archives.java +++ b/core/src/main/java/io/mvnpm/esbuild/util/Archives.java @@ -6,6 +6,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.PosixFilePermission; +import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -52,6 +54,11 @@ private static void extractEntry(Path target, String name, boolean isDirectory, } Files.copy(is, newPath, StandardCopyOption.REPLACE_EXISTING); + if (newPath.toString().contains("/bin/")) { + Set permissions = Files.getPosixFilePermissions(newPath); + permissions.add(PosixFilePermission.OWNER_EXECUTE); + Files.setPosixFilePermissions(newPath, permissions); + } } } diff --git a/core/src/main/resources-filtered/esbuild-java-version.properties b/core/src/main/resources-filtered/esbuild-java-version.properties index 1254bac2..05b6a006 100644 --- a/core/src/main/resources-filtered/esbuild-java-version.properties +++ b/core/src/main/resources-filtered/esbuild-java-version.properties @@ -1 +1 @@ -esbuild.version=${esbuild.version} \ No newline at end of file +esbuild.version=${esbuild.version} diff --git a/core/src/test/java/io/mvnpm/esbuild/BundlerTest.java b/core/src/test/java/io/mvnpm/esbuild/BundlerTest.java deleted file mode 100644 index a18b3ab0..00000000 --- a/core/src/test/java/io/mvnpm/esbuild/BundlerTest.java +++ /dev/null @@ -1,274 +0,0 @@ -package io.mvnpm.esbuild; - -import static org.junit.jupiter.api.Assertions.*; - -import java.io.File; -import java.io.IOException; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; - -import org.junit.jupiter.api.Test; - -import io.mvnpm.esbuild.model.*; -import io.mvnpm.esbuild.model.WebDependency.WebDependencyType; - -public class BundlerTest { - - @Test - public void shouldBundleMvnpm() throws URISyntaxException, IOException { - executeTest(List.of("/mvnpm/stimulus-3.2.1.jar"), WebDependencyType.MVNPM, "application-mvnpm.js", true); - } - - @Test - public void shouldBundleMvnpmSources() throws URISyntaxException, IOException { - executeTest(List.of("/mvnpm/moment-2.29.4-sources.jar"), WebDependencyType.MVNPM, - "application-mvnpm.ts", true); - } - - @Test - void shouldBundleWithScss() throws IOException, URISyntaxException { - final Path root = new File(getClass().getResource("/scss/").toURI()).toPath(); - - final BundleOptions options = BundleOptions.builder() - .withWorkDir(root) - .withEsConfig(EsBuildConfig.builder().entryNames("[name]").build()) - .withDependencies(getJars(List.of("/mvnpm/stimulus-3.2.1.jar", "/mvnpm/bootstrap-5.2.3.jar")), - WebDependencyType.MVNPM) - .addEntryPoint("app.js").build(); - - Bundler.bundle(options, true); - - assertTrue(options.workDir().resolve("dist").resolve("app.js").toFile().exists()); - assertTrue(options.workDir().resolve("dist").resolve("app.css").toFile().exists()); - } - - @Test - void shouldBundleWithScssImports() throws IOException, URISyntaxException { - final Path root = new File(getClass().getResource("/scss/").toURI()).toPath(); - - final BundleOptions options = BundleOptions.builder() - .withWorkDir(root) - .withEsConfig(EsBuildConfig.builder().entryNames("[name]").build()) - .addEntryPoint("import.scss").build(); - - Bundler.bundle(options, true); - - assertTrue(options.workDir().resolve("dist").resolve("import.css").toFile().exists()); - } - - @Test - void shouldBundleWithScssImportsFilesBeforeFolders() throws IOException, URISyntaxException { - final Path root = new File(getClass().getResource("/scss/partials").toURI()).toPath(); - - final BundleOptions options = BundleOptions.builder() - .withWorkDir(root) - .withEsConfig(EsBuildConfig.builder().entryNames("[name]").build()) - .addEntryPoint("test.scss").build(); - - Bundler.bundle(options, true); - - assertTrue(options.workDir().resolve("dist").resolve("test.css").toFile().exists()); - } - - @Test - public void shouldBundleMvnpmAndCreatePackageJson() throws URISyntaxException, IOException { - executeTest(List.of("/mvnpm/stimulus-3.2.0.jar"), WebDependencyType.MVNPM, "application-mvnpm.js", true); - } - - @Test - public void shouldBundleMvnpmWithoutPackageJson() throws URISyntaxException, IOException { - executeTest(List.of("/mvnpm/polymer-3.5.1.jar"), WebDependencyType.MVNPM, "application-mvnpm-importmap.js", true); - } - - @Test - public void shouldBundle() throws URISyntaxException, IOException { - executeTest(List.of("/webjars/htmx.org-1.8.4.jar"), WebDependencyType.WEBJARS, "application-webjar.js", true); - } - - @Test - public void shouldWatchExitWithError() throws URISyntaxException, IOException, InterruptedException { - final BundleOptions options = getBundleOptions( - List.of("/mvnpm/stimulus-3.2.1.jar"), - WebDependencyType.MVNPM, - "application-mvnpm.js").withEsConfig( - EsBuildConfig.builder() - .fixedEntryNames() - .define("foo", "\"bar") // <- looks like you might be missing a closing quote here? - .build()) - .build(); - - ExecutorService executor = Executors.newFixedThreadPool(10); - AtomicReference r = new AtomicReference<>(); - try { - for (int i = 0; i < 100; i++) { - final int idx = i; - executor.submit(() -> { - System.out.println("Running task " + idx); - final Watch watch; - try { - watch = Bundler.watch(options, (wr) -> { - }, true); - r.set(watch); - } catch (IOException e) { - // good - } - - }); - } - } finally { - executor.shutdown(); - } - executor.awaitTermination(3, TimeUnit.SECONDS); - if (r.get() != null) { - throw new RuntimeException(r.get().toString()); - } - } - - @Test - public void shouldWatch() throws URISyntaxException, IOException, InterruptedException { - // given - final BundleOptions options = getBundleOptions(List.of("/mvnpm/stimulus-3.2.1.jar"), - WebDependencyType.MVNPM, - "application-mvnpm.js").withEsConfig(EsBuildConfig.builder().fixedEntryNames().build()).build(); - - // when - AtomicReference latch = new AtomicReference<>(new CountDownLatch(1)); - AtomicReference bundleException = new AtomicReference<>(); - final Watch watch = Bundler.watch(options, (r) -> { - if (!r.isSuccess()) { - bundleException.set(r.bundleException()); - } - latch.get().countDown(); - }, true); - - // then - assertTrue(latch.get().getCount() == 1, "First build is not using the listener"); - assertTrue(watch.firstBuildResult().isSuccess(), "first build is success"); - assertTrue(watch.isAlive(), "process is alive"); - final Path app = watch.workDir().resolve("application-mvnpm.js"); - assertTrue(Files.exists(app)); - final Path distApp = watch.dist().resolve("application-mvnpm.js"); - assertTrue(Files.exists(distApp)); - - // when - Files.writeString(app, "\nalert(\"foo\");", StandardOpenOption.APPEND); - assertTrue(latch.get().await(2, TimeUnit.SECONDS)); - assertNull(bundleException.get(), "No error during bundling"); - assertTrue(Files.readString(distApp).contains("alert(\"foo\");")); - - // then - watch.close(); - - assertFalse(watch.isAlive()); - } - - @Test - public void shouldWatchWithError() throws URISyntaxException, IOException, InterruptedException { - - // given - final BundleOptions options = getBundleOptions(List.of("/mvnpm/stimulus-3.2.1.jar"), - WebDependencyType.MVNPM, - "application-error.js").withEsConfig(EsBuildConfig.builder().fixedEntryNames().build()).build(); - - // when - AtomicReference latch = new AtomicReference<>(new CountDownLatch(1)); - AtomicReference bundleException = new AtomicReference<>(); - final Watch watch = Bundler.watch(options, (r) -> { - if (!r.isSuccess()) { - bundleException.set(r.bundleException()); - } - latch.get().countDown(); - }, true); - - // then - assertTrue(latch.get().getCount() == 1, "First build is not using the listener"); - assertTrue(watch.isAlive(), "process is alive"); - assertNotNull(watch.firstBuildResult().bundleException(), "Error during bundling"); - assertTrue(watch.firstBuildResult().bundleException().output().contains("[ERROR] Could not resolve \"\"")); - - final Path app = watch.workDir().resolve("application-error.js"); - assertTrue(Files.exists(app)); - - // when - Files.writeString(app, "alert(\"foo\");", StandardOpenOption.TRUNCATE_EXISTING); - - // then - assertTrue(latch.get().await(2, TimeUnit.SECONDS)); - assertNull(bundleException.get(), "No error during bundling"); - final Path distApp = watch.dist().resolve("application-error.js"); - assertTrue(Files.exists(distApp)); - assertTrue(Files.readString(distApp).contains("alert(\"foo\");")); - - // then - watch.close(); - } - - @Test - public void shouldThrowException() { - assertThrows(BundleException.class, () -> { - executeTest(List.of("/mvnpm/stimulus-3.2.1.jar"), WebDependencyType.MVNPM, "application-error.js", false); - }); - } - - @Test - public void shouldResolveRelativeFolders() throws URISyntaxException, IOException { - // given - final Path root = new File(getClass().getResource("/path/").toURI()).toPath(); - final BundleOptions bundleOptions = BundleOptions.builder().withWorkDir(root) - .addAutoEntryPoint(root, "main", List.of("foo/bar.js")).build(); - - // when - final BundleResult result = Bundler.bundle(bundleOptions, true); - - // then - assertTrue(result.dist().toFile().exists()); - } - - private void executeTest(List jarNames, WebDependencyType type, String scriptName, boolean check) - throws URISyntaxException, IOException { - final BundleOptions bundleOptions = getBundleOptions(jarNames, type, scriptName).build(); - final BundleResult result = Bundler.bundle(bundleOptions, true); - - if (check) { - assertTrue(result.dist().toFile().exists()); - } - - } - - private BundleOptionsBuilder getBundleOptions(List jarNames, WebDependencyType type, String scriptName) - throws URISyntaxException { - final List jars = getJars(jarNames); - final Path rootDir = new File(getClass().getResource("/").toURI()).toPath(); - final BundleOptionsBuilder bundleOptionsBuilder = BundleOptions.builder().withDependencies(jars, type); - if (scriptName != null) { - bundleOptionsBuilder - .addEntryPoint(rootDir, scriptName); - } - return bundleOptionsBuilder; - } - - private List getJars(List jarNames) { - final List jars = jarNames.stream().map(jarName -> { - try { - final URL resource = getClass().getResource(jarName); - if (resource == null) { - throw new RuntimeException("Could not find resource: " + jarName); - } - return new File(resource.toURI()).toPath(); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - }).toList(); - return jars; - } - -} diff --git a/core/src/test/java/io/mvnpm/esbuild/ExecuteTest.java b/core/src/test/java/io/mvnpm/esbuild/ExecuteTest.java deleted file mode 100644 index 511b3c96..00000000 --- a/core/src/test/java/io/mvnpm/esbuild/ExecuteTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.mvnpm.esbuild; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; - -import org.junit.jupiter.api.Test; - -import io.mvnpm.esbuild.model.EsBuildConfig; -import io.mvnpm.esbuild.model.EsBuildConfigBuilder; -import io.mvnpm.esbuild.model.ExecuteResult; -import io.mvnpm.esbuild.resolve.Resolver; - -public class ExecuteTest { - - @Test - public void shouldExecuteEsBuild() throws IOException { - final EsBuildConfigBuilder esBuildConfig = EsBuildConfig.builder(); - esBuildConfig.version(true); - final String defaultVersion = Bundler.ESBUILD_EMBEDDED_VERSION; - final Path path = Resolver.create().resolve(defaultVersion); - String workingDirectory = System.getProperty("user.dir"); - final ExecuteResult executeResult = new Execute(Paths.get(workingDirectory), path.toFile(), esBuildConfig.build()) - .executeAndWait(); - assertEquals(defaultVersion + "\n", executeResult.output()); - } -} diff --git a/core/src/test/java/io/mvnpm/esbuild/resolve/BundleTester.java b/core/src/test/java/io/mvnpm/esbuild/resolve/BundleTester.java deleted file mode 100644 index 9926effa..00000000 --- a/core/src/test/java/io/mvnpm/esbuild/resolve/BundleTester.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.mvnpm.esbuild.resolve; - -import java.io.IOException; - -import io.mvnpm.esbuild.Bundler; -import io.mvnpm.esbuild.util.PathUtils; - -public abstract class BundleTester { - - public static void cleanUp(String version) throws IOException { - PathUtils.deleteRecursive(Resolvers.getLocation(version)); - } - - public static void cleanUpDefault() throws IOException { - cleanUp(Bundler.ESBUILD_EMBEDDED_VERSION); - } -} diff --git a/core/src/test/resources/application-mvnpm.js b/core/src/test/resources/application-mvnpm.js deleted file mode 100644 index 1b5d3ef2..00000000 --- a/core/src/test/resources/application-mvnpm.js +++ /dev/null @@ -1,3 +0,0 @@ -import { Application } from "@hotwired/stimulus"; - -Application.start(); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..586738ed --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1515 @@ +{ + "name": "esbuild-java", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "esbuild-java", + "version": "1.0.0", + "dependencies": { + "esbuild-sass-plugin": "^3.3.1" + } + }, + "node_modules/@bufbuild/protobuf": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.9.0.tgz", + "integrity": "sha512-rnJenoStJ8nvmt9Gzye8nkYd6V22xUAnu4086ER7h1zJ508vStko4pMvDeQ446ilDTFpV5wnoc5YS7XvMwwMqA==", + "license": "(Apache-2.0 AND BSD-3-Clause)", + "peer": true + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", + "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz", + "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", + "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz", + "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", + "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", + "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", + "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", + "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", + "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", + "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", + "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", + "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", + "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", + "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", + "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", + "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", + "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz", + "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", + "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz", + "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", + "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz", + "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", + "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", + "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", + "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", + "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "optional": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer-builder": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/buffer-builder/-/buffer-builder-0.2.0.tgz", + "integrity": "sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg==", + "license": "MIT/X11", + "peer": true + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/colorjs.io": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.5.2.tgz", + "integrity": "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==", + "license": "MIT", + "peer": true + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esbuild": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", + "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.10", + "@esbuild/android-arm": "0.25.10", + "@esbuild/android-arm64": "0.25.10", + "@esbuild/android-x64": "0.25.10", + "@esbuild/darwin-arm64": "0.25.10", + "@esbuild/darwin-x64": "0.25.10", + "@esbuild/freebsd-arm64": "0.25.10", + "@esbuild/freebsd-x64": "0.25.10", + "@esbuild/linux-arm": "0.25.10", + "@esbuild/linux-arm64": "0.25.10", + "@esbuild/linux-ia32": "0.25.10", + "@esbuild/linux-loong64": "0.25.10", + "@esbuild/linux-mips64el": "0.25.10", + "@esbuild/linux-ppc64": "0.25.10", + "@esbuild/linux-riscv64": "0.25.10", + "@esbuild/linux-s390x": "0.25.10", + "@esbuild/linux-x64": "0.25.10", + "@esbuild/netbsd-arm64": "0.25.10", + "@esbuild/netbsd-x64": "0.25.10", + "@esbuild/openbsd-arm64": "0.25.10", + "@esbuild/openbsd-x64": "0.25.10", + "@esbuild/openharmony-arm64": "0.25.10", + "@esbuild/sunos-x64": "0.25.10", + "@esbuild/win32-arm64": "0.25.10", + "@esbuild/win32-ia32": "0.25.10", + "@esbuild/win32-x64": "0.25.10" + } + }, + "node_modules/esbuild-sass-plugin": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/esbuild-sass-plugin/-/esbuild-sass-plugin-3.3.1.tgz", + "integrity": "sha512-SnO1ls+d52n6j8gRRpjexXI8MsHEaumS0IdDHaYM29Y6gakzZYMls6i9ql9+AWMSQk/eryndmUpXEgT34QrX1A==", + "license": "MIT", + "dependencies": { + "resolve": "^1.22.8", + "safe-identifier": "^0.4.2", + "sass": "^1.71.1" + }, + "peerDependencies": { + "esbuild": ">=0.20.1", + "sass-embedded": "^1.71.1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "optional": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/immutable": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", + "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==", + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "optional": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "optional": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT", + "optional": true + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-identifier": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/safe-identifier/-/safe-identifier-0.4.2.tgz", + "integrity": "sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==", + "license": "ISC" + }, + "node_modules/sass": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.93.2.tgz", + "integrity": "sha512-t+YPtOQHpGW1QWsh1CHQ5cPIr9lbbGZLZnbihP/D/qZj/yuV68m8qarcV17nvkOX81BCrvzAlq2klCQFZghyTg==", + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/sass-embedded": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.93.2.tgz", + "integrity": "sha512-FvQdkn2dZ8DGiLgi0Uf4zsj7r/BsiLImNa5QJ10eZalY6NfZyjrmWGFcuCN5jNwlDlXFJnftauv+UtvBKLvepQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@bufbuild/protobuf": "^2.5.0", + "buffer-builder": "^0.2.0", + "colorjs.io": "^0.5.0", + "immutable": "^5.0.2", + "rxjs": "^7.4.0", + "supports-color": "^8.1.1", + "sync-child-process": "^1.0.2", + "varint": "^6.0.0" + }, + "bin": { + "sass": "dist/bin/sass.js" + }, + "engines": { + "node": ">=16.0.0" + }, + "optionalDependencies": { + "sass-embedded-all-unknown": "1.93.2", + "sass-embedded-android-arm": "1.93.2", + "sass-embedded-android-arm64": "1.93.2", + "sass-embedded-android-riscv64": "1.93.2", + "sass-embedded-android-x64": "1.93.2", + "sass-embedded-darwin-arm64": "1.93.2", + "sass-embedded-darwin-x64": "1.93.2", + "sass-embedded-linux-arm": "1.93.2", + "sass-embedded-linux-arm64": "1.93.2", + "sass-embedded-linux-musl-arm": "1.93.2", + "sass-embedded-linux-musl-arm64": "1.93.2", + "sass-embedded-linux-musl-riscv64": "1.93.2", + "sass-embedded-linux-musl-x64": "1.93.2", + "sass-embedded-linux-riscv64": "1.93.2", + "sass-embedded-linux-x64": "1.93.2", + "sass-embedded-unknown-all": "1.93.2", + "sass-embedded-win32-arm64": "1.93.2", + "sass-embedded-win32-x64": "1.93.2" + } + }, + "node_modules/sass-embedded-all-unknown": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-all-unknown/-/sass-embedded-all-unknown-1.93.2.tgz", + "integrity": "sha512-GdEuPXIzmhRS5J7UKAwEvtk8YyHQuFZRcpnEnkA3rwRUI27kwjyXkNeIj38XjUQ3DzrfMe8HcKFaqWGHvblS7Q==", + "cpu": [ + "!arm", + "!arm64", + "!riscv64", + "!x64" + ], + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "sass": "1.93.2" + } + }, + "node_modules/sass-embedded-android-arm": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-android-arm/-/sass-embedded-android-arm-1.93.2.tgz", + "integrity": "sha512-I8bpO8meZNo5FvFx5FIiE7DGPVOYft0WjuwcCCdeJ6duwfkl6tZdatex1GrSigvTsuz9L0m4ngDcX/Tj/8yMow==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-android-arm64": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.93.2.tgz", + "integrity": "sha512-346f4iVGAPGcNP6V6IOOFkN5qnArAoXNTPr5eA/rmNpeGwomdb7kJyQ717r9rbJXxOG8OAAUado6J0qLsjnjXQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-android-riscv64": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-android-riscv64/-/sass-embedded-android-riscv64-1.93.2.tgz", + "integrity": "sha512-hSMW1s4yJf5guT9mrdkumluqrwh7BjbZ4MbBW9tmi1DRDdlw1Wh9Oy1HnnmOG8x9XcI1qkojtPL6LUuEJmsiDg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-android-x64": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-android-x64/-/sass-embedded-android-x64-1.93.2.tgz", + "integrity": "sha512-JqktiHZduvn+ldGBosE40ALgQ//tGCVNAObgcQ6UIZznEJbsHegqStqhRo8UW3x2cgOO2XYJcrInH6cc7wdKbw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-darwin-arm64": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.93.2.tgz", + "integrity": "sha512-qI1X16qKNeBJp+M/5BNW7v/JHCDYWr1/mdoJ7+UMHmP0b5AVudIZtimtK0hnjrLnBECURifd6IkulybR+h+4UA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-darwin-x64": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.93.2.tgz", + "integrity": "sha512-4KeAvlkQ0m0enKUnDGQJZwpovYw99iiMb8CTZRSsQm8Eh7halbJZVmx67f4heFY/zISgVOCcxNg19GrM5NTwtA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-arm": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.93.2.tgz", + "integrity": "sha512-N3+D/ToHtzwLDO+lSH05Wo6/KRxFBPnbjVHASOlHzqJnK+g5cqex7IFAp6ozzlRStySk61Rp6d+YGrqZ6/P0PA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-arm64": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.93.2.tgz", + "integrity": "sha512-9ftX6nd5CsShJqJ2WRg+ptaYvUW+spqZfJ88FbcKQBNFQm6L87luj3UI1rB6cP5EWrLwHA754OKxRJyzWiaN6g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-musl-arm": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.93.2.tgz", + "integrity": "sha512-XBTvx66yRenvEsp3VaJCb3HQSyqCsUh7R+pbxcN5TuzueybZi0LXvn9zneksdXcmjACMlMpIVXi6LyHPQkYc8A==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-musl-arm64": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.93.2.tgz", + "integrity": "sha512-+3EHuDPkMiAX5kytsjEC1bKZCawB9J6pm2eBIzzLMPWbf5xdx++vO1DpT7hD4bm4ZGn0eVHgSOKIfP6CVz6tVg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-musl-riscv64": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-riscv64/-/sass-embedded-linux-musl-riscv64-1.93.2.tgz", + "integrity": "sha512-0sB5kmVZDKTYzmCSlTUnjh6mzOhzmQiW/NNI5g8JS4JiHw2sDNTvt1dsFTuqFkUHyEOY3ESTsfHHBQV8Ip4bEA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-musl-x64": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.93.2.tgz", + "integrity": "sha512-t3ejQ+1LEVuHy7JHBI2tWHhoMfhedUNDjGJR2FKaLgrtJntGnyD1RyX0xb3nuqL/UXiEAtmTmZY+Uh3SLUe1Hg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-riscv64": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-riscv64/-/sass-embedded-linux-riscv64-1.93.2.tgz", + "integrity": "sha512-e7AndEwAbFtXaLy6on4BfNGTr3wtGZQmypUgYpSNVcYDO+CWxatKVY4cxbehMPhxG9g5ru+eaMfynvhZt7fLaA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-x64": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.93.2.tgz", + "integrity": "sha512-U3EIUZQL11DU0xDDHXexd4PYPHQaSQa2hzc4EzmhHqrAj+TyfYO94htjWOd+DdTPtSwmLp+9cTWwPZBODzC96w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-unknown-all": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-unknown-all/-/sass-embedded-unknown-all-1.93.2.tgz", + "integrity": "sha512-7VnaOmyewcXohiuoFagJ3SK5ddP9yXpU0rzz+pZQmS1/+5O6vzyFCUoEt3HDRaLctH4GT3nUGoK1jg0ae62IfQ==", + "license": "MIT", + "optional": true, + "os": [ + "!android", + "!darwin", + "!linux", + "!win32" + ], + "peer": true, + "dependencies": { + "sass": "1.93.2" + } + }, + "node_modules/sass-embedded-win32-arm64": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.93.2.tgz", + "integrity": "sha512-Y90DZDbQvtv4Bt0GTXKlcT9pn4pz8AObEjFF8eyul+/boXwyptPZ/A1EyziAeNaIEIfxyy87z78PUgCeGHsx3Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-win32-x64": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.93.2.tgz", + "integrity": "sha512-BbSucRP6PVRZGIwlEBkp+6VQl2GWdkWFMN+9EuOTPrLxCJZoq+yhzmbjspd3PeM8+7WJ7AdFu/uRYdO8tor1iQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sync-child-process": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/sync-child-process/-/sync-child-process-1.0.2.tgz", + "integrity": "sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==", + "license": "MIT", + "peer": true, + "dependencies": { + "sync-message-port": "^1.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/sync-message-port": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sync-message-port/-/sync-message-port-1.1.3.tgz", + "integrity": "sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD", + "peer": true + }, + "node_modules/varint": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz", + "integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==", + "license": "MIT", + "peer": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..7ffe6a77 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "esbuild-java", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://github.com/mvnpm/esbuild-java" + }, + "private": true, + "dependencies": { + "esbuild-sass-plugin": "^3.3.1" + } +} diff --git a/plugin-sass/pom.xml b/plugin-sass/pom.xml new file mode 100644 index 00000000..345b9d1f --- /dev/null +++ b/plugin-sass/pom.xml @@ -0,0 +1,216 @@ + + + 4.0.0 + + io.mvnpm + esbuild-java-parent + 999-SNAPSHOT + + esbuild-java-plugin-sass + Esbuild Java - Plugin Sass + Sass plugin for Esbuild Java + + 3.3.1 + 1.89.1 + + + + io.mvnpm + esbuild-java + ${project.version} + + + + org.mvnpm + esbuild-sass-plugin + ${esbuild-sass-plugin.version} + + + org.mvnpm + sass + ${sass.version} + + + io.mvnpm + esbuild-java-testing + ${project.version} + test + + + + + locker + + + !unlocked + + + + + + org.mvnpm.at.bufbuild + protobuf + 2.9.0 + + + org.mvnpm.at.esbuild + darwin-arm64 + 0.25.10 + + + org.mvnpm.at.esbuild + linux-arm + 0.25.10 + + + org.mvnpm + buffer-builder + 0.2.0 + + + org.mvnpm + chokidar + 4.0.3 + + + org.mvnpm + colorjs.io + 0.5.2 + + + org.mvnpm + esbuild-sass-plugin + 3.3.1 + + + org.mvnpm + esbuild + 0.25.10 + + + org.mvnpm + function-bind + 1.1.2 + + + org.mvnpm + has-flag + 4.0.0 + + + org.mvnpm + hasown + 2.0.2 + + + org.mvnpm + immutable + 5.1.2 + + + org.mvnpm + is-core-module + 2.16.1 + + + org.mvnpm + path-parse + 1.0.7 + + + org.mvnpm + readdirp + 4.1.2 + + + org.mvnpm + resolve + 1.22.10 + + + org.mvnpm + rxjs + 7.8.2 + + + org.mvnpm + safe-identifier + 0.4.2 + + + org.mvnpm + sass-embedded + 1.93.2 + + + org.mvnpm + sass + 1.89.1 + + + org.mvnpm + source-map-js + 1.2.1 + + + org.mvnpm + supports-color + 8.1.1 + + + org.mvnpm + supports-preserve-symlinks-flag + 1.0.0 + + + org.mvnpm + sync-child-process + 1.0.2 + + + org.mvnpm + sync-message-port + 1.1.3 + + + org.mvnpm + tslib + 2.8.1 + + + org.mvnpm + varint + 6.0.0 + + + + + + + + + maven-surefire-plugin + + + + java.util.logging.config.file + src/test/resources/logging.properties + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + net.revelc.code.formatter + formatter-maven-plugin + + + net.revelc.code + impsort-maven-plugin + + + + diff --git a/plugin-sass/src/test/java/io/mvnpm/esbuild/BundlerScssTest.java b/plugin-sass/src/test/java/io/mvnpm/esbuild/BundlerScssTest.java new file mode 100644 index 00000000..7bab0764 --- /dev/null +++ b/plugin-sass/src/test/java/io/mvnpm/esbuild/BundlerScssTest.java @@ -0,0 +1,94 @@ +package io.mvnpm.esbuild; + +import static io.mvnpm.esbuild.BundlerTestHelper.getJars; +import static org.junit.jupiter.api.Assertions.*; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import org.apache.commons.io.FileUtils; +import org.junit.jupiter.api.Test; + +import io.mvnpm.esbuild.model.BundleOptions; +import io.mvnpm.esbuild.model.DevResult; +import io.mvnpm.esbuild.model.EsBuildConfig; +import io.mvnpm.esbuild.model.WebDependency.WebDependencyType; + +public class BundlerScssTest { + + @Test + void shouldBundleWithScss() throws IOException, URISyntaxException { + + final Path root = prepareTest(); + + final BundleOptions options = BundleOptions.builder() + .withWorkDir(root) + .withEsConfig(EsBuildConfig.builder().entryNames("[name]").build()) + .withDependencies(getJars(List.of("/mvnpm/stimulus-3.2.1.jar", "/mvnpm/bootstrap-5.2.3.jar")), + WebDependencyType.MVNPM) + .addEntryPoint("app.js").build(); + + Bundler.bundle(options, true); + + assertTrue(options.workDir().resolve("dist").resolve("app.js").toFile().exists()); + assertTrue(options.workDir().resolve("dist").resolve("app.css").toFile().exists()); + } + + @Test + void shouldBundleInDevWithScss() throws IOException, URISyntaxException { + final Path root = prepareTest(); + + final BundleOptions options = BundleOptions.builder() + .withWorkDir(root) + .withEsConfig(EsBuildConfig.builder().entryNames("[name]").build()) + .withDependencies(getJars(List.of("/mvnpm/stimulus-3.2.1.jar", "/mvnpm/bootstrap-5.2.3.jar")), + WebDependencyType.MVNPM) + .addEntryPoint("app.js").build(); + + try (DevResult dev = Bundler.dev(options, true)) { + dev.process().build(); + } + assertTrue(options.workDir().resolve("dist").resolve("app.js").toFile().exists()); + assertTrue(options.workDir().resolve("dist").resolve("app.css").toFile().exists()); + } + + @Test + void shouldBundleWithScssImports() throws IOException, URISyntaxException { + final Path root = prepareTest(); + + final BundleOptions options = BundleOptions.builder() + .withWorkDir(root) + .withEsConfig(EsBuildConfig.builder().entryNames("[name]").build()) + .addEntryPoint("import.scss").build(); + + Bundler.bundle(options, true); + + assertTrue(options.workDir().resolve("dist").resolve("import.css").toFile().exists()); + } + + @Test + void shouldBundleWithScssImportsFilesBeforeFolders() throws IOException, URISyntaxException { + final Path root = prepareTest(); + + final BundleOptions options = BundleOptions.builder() + .withWorkDir(root) + .withEsConfig(EsBuildConfig.builder().entryNames("[name]").build()) + .addEntryPoint("partials/test.scss").build(); + + Bundler.bundle(options, true); + + assertTrue(options.workDir().resolve("dist").resolve("test.css").toFile().exists()); + } + + static Path prepareTest() throws IOException, URISyntaxException { + final Path temp = Files.createTempDirectory("test-scss"); + final Path root = new File(BundlerScssTest.class.getResource("/scss/").toURI()).toPath(); + FileUtils.copyDirectory(root.toFile(), temp.toFile()); + return temp; + } + +} diff --git a/core/src/test/resources/scss/_style2.scss b/plugin-sass/src/test/resources/scss/_style2.scss similarity index 100% rename from core/src/test/resources/scss/_style2.scss rename to plugin-sass/src/test/resources/scss/_style2.scss diff --git a/core/src/test/resources/scss/app.js b/plugin-sass/src/test/resources/scss/app.js similarity index 100% rename from core/src/test/resources/scss/app.js rename to plugin-sass/src/test/resources/scss/app.js diff --git a/core/src/test/resources/scss/import.scss b/plugin-sass/src/test/resources/scss/import.scss similarity index 100% rename from core/src/test/resources/scss/import.scss rename to plugin-sass/src/test/resources/scss/import.scss diff --git a/core/src/test/resources/scss/partials/_meta.scss b/plugin-sass/src/test/resources/scss/partials/_meta.scss similarity index 100% rename from core/src/test/resources/scss/partials/_meta.scss rename to plugin-sass/src/test/resources/scss/partials/_meta.scss diff --git a/core/src/test/resources/scss/partials/_relative.scss b/plugin-sass/src/test/resources/scss/partials/_relative.scss similarity index 100% rename from core/src/test/resources/scss/partials/_relative.scss rename to plugin-sass/src/test/resources/scss/partials/_relative.scss diff --git a/core/src/test/resources/scss/partials/meta/what.scss b/plugin-sass/src/test/resources/scss/partials/meta/what.scss similarity index 100% rename from core/src/test/resources/scss/partials/meta/what.scss rename to plugin-sass/src/test/resources/scss/partials/meta/what.scss diff --git a/core/src/test/resources/scss/partials/test.scss b/plugin-sass/src/test/resources/scss/partials/test.scss similarity index 100% rename from core/src/test/resources/scss/partials/test.scss rename to plugin-sass/src/test/resources/scss/partials/test.scss diff --git a/core/src/test/resources/scss/style.scss b/plugin-sass/src/test/resources/scss/style.scss similarity index 100% rename from core/src/test/resources/scss/style.scss rename to plugin-sass/src/test/resources/scss/style.scss diff --git a/core/src/test/resources/scss/test/index.scss b/plugin-sass/src/test/resources/scss/test/index.scss similarity index 100% rename from core/src/test/resources/scss/test/index.scss rename to plugin-sass/src/test/resources/scss/test/index.scss diff --git a/pom.xml b/pom.xml index 26ccc896..594d5133 100644 --- a/pom.xml +++ b/pom.xml @@ -4,15 +4,15 @@ io.mvnpm esbuild-java-parent - Esbuild wrapper for Java - parent + Esbuild Java - parent 999-SNAPSHOT Small wrapper around esbuild to be able to use it in Java pom + testing core - bundle-scss - bundle-original + plugin-sass https://github.com/mvnpm/esbuild-java/ diff --git a/testing/pom.xml b/testing/pom.xml new file mode 100644 index 00000000..d8190c2c --- /dev/null +++ b/testing/pom.xml @@ -0,0 +1,31 @@ + + 4.0.0 + + io.mvnpm + esbuild-java-parent + 999-SNAPSHOT + + esbuild-java-testing + Esbuild Java - testing + + + + + + io.mvnpm + esbuild-java + ${project.version} + + + + org.junit.jupiter + junit-jupiter-engine + + + org.hamcrest + hamcrest-all + 1.3 + + + diff --git a/testing/src/main/java/io/mvnpm/esbuild/BundlerTestHelper.java b/testing/src/main/java/io/mvnpm/esbuild/BundlerTestHelper.java new file mode 100644 index 00000000..46367578 --- /dev/null +++ b/testing/src/main/java/io/mvnpm/esbuild/BundlerTestHelper.java @@ -0,0 +1,70 @@ +package io.mvnpm.esbuild; + +import io.mvnpm.esbuild.model.BundleOptions; +import io.mvnpm.esbuild.model.BundleOptionsBuilder; +import io.mvnpm.esbuild.model.BundleResult; +import io.mvnpm.esbuild.model.WebDependency.WebDependencyType; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class BundlerTestHelper { + + public static void executeTest(List jarNames, WebDependencyType type, String scriptName, boolean check) + throws URISyntaxException, IOException { + final BundleOptions bundleOptions = getBundleOptions(jarNames, type, scriptName).build(); + final BundleResult result = Bundler.bundle(bundleOptions, true); + + if (check) { + assertTrue(result.dist().toFile().exists()); + } + + } + + + public static BundleOptionsBuilder getBundleOptions(List jarNames, WebDependencyType type, String scriptName) + throws URISyntaxException, IOException { + final List jars = getJars(jarNames); + final Path tempDir = Files.createTempDirectory("esbuild-test"); + final BundleOptionsBuilder bundleOptionsBuilder = BundleOptions.builder().withDependencies(jars, type); + if (scriptName != null) { + Files.copy(new File(BundlerTestHelper.class.getResource("/" + scriptName).toURI()).toPath(), tempDir.resolve(scriptName)); + bundleOptionsBuilder + .addEntryPoint(tempDir, scriptName); + } + return bundleOptionsBuilder; + } + + public static List getJars(List jarNames) { + final List jars = jarNames.stream().map(jarName -> { + try (InputStream inputStream = BundlerTestHelper.class.getResourceAsStream(jarName)) { + if (inputStream == null) { + throw new IllegalArgumentException("Resource not found: " + jarName); + } + + String fileName = Paths.get(jarName).getFileName().toString(); + Path tempFile = Files.createTempFile("extracted-", "-" + fileName); + tempFile.toFile().deleteOnExit(); + + Files.copy(inputStream, tempFile, StandardCopyOption.REPLACE_EXISTING); + + return tempFile; + } catch (IOException e) { + throw new UncheckedIOException("Failed to copy resource to temp file", e); + } + }).toList(); + return jars; + } + +} diff --git a/core/src/test/resources/application-error.js b/testing/src/main/resources/application-error.js similarity index 100% rename from core/src/test/resources/application-error.js rename to testing/src/main/resources/application-error.js diff --git a/core/src/test/resources/application-mvnpm-importmap.js b/testing/src/main/resources/application-mvnpm-importmap.js similarity index 100% rename from core/src/test/resources/application-mvnpm-importmap.js rename to testing/src/main/resources/application-mvnpm-importmap.js diff --git a/core/src/test/resources/application-mvnpm.ts b/testing/src/main/resources/application-mvnpm.ts similarity index 100% rename from core/src/test/resources/application-mvnpm.ts rename to testing/src/main/resources/application-mvnpm.ts diff --git a/core/src/test/resources/application-webjar.js b/testing/src/main/resources/application-webjar.js similarity index 100% rename from core/src/test/resources/application-webjar.js rename to testing/src/main/resources/application-webjar.js diff --git a/core/src/test/resources/babel-runtime/importmap.json b/testing/src/main/resources/babel-runtime/importmap.json similarity index 100% rename from core/src/test/resources/babel-runtime/importmap.json rename to testing/src/main/resources/babel-runtime/importmap.json diff --git a/core/src/test/resources/babel-runtime/resources/_static/babel-runtime/LICENSE b/testing/src/main/resources/babel-runtime/resources/_static/babel-runtime/LICENSE similarity index 100% rename from core/src/test/resources/babel-runtime/resources/_static/babel-runtime/LICENSE rename to testing/src/main/resources/babel-runtime/resources/_static/babel-runtime/LICENSE diff --git a/core/src/test/resources/changed/application-mvnpm.js b/testing/src/main/resources/changed/application-mvnpm.js similarity index 100% rename from core/src/test/resources/changed/application-mvnpm.js rename to testing/src/main/resources/changed/application-mvnpm.js diff --git a/core/src/test/resources/logging.properties b/testing/src/main/resources/logging.properties similarity index 100% rename from core/src/test/resources/logging.properties rename to testing/src/main/resources/logging.properties diff --git a/core/src/test/resources/multi/script1.js b/testing/src/main/resources/multi/script1.js similarity index 100% rename from core/src/test/resources/multi/script1.js rename to testing/src/main/resources/multi/script1.js diff --git a/core/src/test/resources/multi/script2-test.js b/testing/src/main/resources/multi/script2-test.js similarity index 100% rename from core/src/test/resources/multi/script2-test.js rename to testing/src/main/resources/multi/script2-test.js diff --git a/core/src/test/resources/multi/style.css b/testing/src/main/resources/multi/style.css similarity index 100% rename from core/src/test/resources/multi/style.css rename to testing/src/main/resources/multi/style.css diff --git a/core/src/test/resources/multi/sub/sub.js b/testing/src/main/resources/multi/sub/sub.js similarity index 100% rename from core/src/test/resources/multi/sub/sub.js rename to testing/src/main/resources/multi/sub/sub.js diff --git a/core/src/test/resources/mvnpm/bootstrap-5.2.3.jar b/testing/src/main/resources/mvnpm/bootstrap-5.2.3.jar similarity index 100% rename from core/src/test/resources/mvnpm/bootstrap-5.2.3.jar rename to testing/src/main/resources/mvnpm/bootstrap-5.2.3.jar diff --git a/core/src/test/resources/mvnpm/hooks-0.4.9.jar b/testing/src/main/resources/mvnpm/hooks-0.4.9.jar similarity index 100% rename from core/src/test/resources/mvnpm/hooks-0.4.9.jar rename to testing/src/main/resources/mvnpm/hooks-0.4.9.jar diff --git a/core/src/test/resources/mvnpm/jquery-3.7.1.jar b/testing/src/main/resources/mvnpm/jquery-3.7.1.jar similarity index 100% rename from core/src/test/resources/mvnpm/jquery-3.7.1.jar rename to testing/src/main/resources/mvnpm/jquery-3.7.1.jar diff --git a/core/src/test/resources/mvnpm/lit-3.1.0.jar b/testing/src/main/resources/mvnpm/lit-3.1.0.jar similarity index 100% rename from core/src/test/resources/mvnpm/lit-3.1.0.jar rename to testing/src/main/resources/mvnpm/lit-3.1.0.jar diff --git a/core/src/test/resources/mvnpm/moment-2.29.4-sources.jar b/testing/src/main/resources/mvnpm/moment-2.29.4-sources.jar similarity index 100% rename from core/src/test/resources/mvnpm/moment-2.29.4-sources.jar rename to testing/src/main/resources/mvnpm/moment-2.29.4-sources.jar diff --git a/core/src/test/resources/mvnpm/polymer-3.5.1.jar b/testing/src/main/resources/mvnpm/polymer-3.5.1.jar similarity index 100% rename from core/src/test/resources/mvnpm/polymer-3.5.1.jar rename to testing/src/main/resources/mvnpm/polymer-3.5.1.jar diff --git a/core/src/test/resources/mvnpm/react-bootstrap-2.7.4.jar b/testing/src/main/resources/mvnpm/react-bootstrap-2.7.4.jar similarity index 100% rename from core/src/test/resources/mvnpm/react-bootstrap-2.7.4.jar rename to testing/src/main/resources/mvnpm/react-bootstrap-2.7.4.jar diff --git a/core/src/test/resources/mvnpm/stimulus-3.2.0.jar b/testing/src/main/resources/mvnpm/stimulus-3.2.0.jar similarity index 100% rename from core/src/test/resources/mvnpm/stimulus-3.2.0.jar rename to testing/src/main/resources/mvnpm/stimulus-3.2.0.jar diff --git a/core/src/test/resources/mvnpm/stimulus-3.2.1.jar b/testing/src/main/resources/mvnpm/stimulus-3.2.1.jar similarity index 100% rename from core/src/test/resources/mvnpm/stimulus-3.2.1.jar rename to testing/src/main/resources/mvnpm/stimulus-3.2.1.jar diff --git a/core/src/test/resources/mvnpm/vaadin-webcomponents-24.1.6.jar b/testing/src/main/resources/mvnpm/vaadin-webcomponents-24.1.6.jar similarity index 100% rename from core/src/test/resources/mvnpm/vaadin-webcomponents-24.1.6.jar rename to testing/src/main/resources/mvnpm/vaadin-webcomponents-24.1.6.jar diff --git a/core/src/test/resources/mvnpm/vaadin-webcomponents-24.3.5.jar b/testing/src/main/resources/mvnpm/vaadin-webcomponents-24.3.5.jar similarity index 100% rename from core/src/test/resources/mvnpm/vaadin-webcomponents-24.3.5.jar rename to testing/src/main/resources/mvnpm/vaadin-webcomponents-24.3.5.jar diff --git a/core/src/test/resources/path/baz.js b/testing/src/main/resources/path/baz.js similarity index 100% rename from core/src/test/resources/path/baz.js rename to testing/src/main/resources/path/baz.js diff --git a/core/src/test/resources/path/foo/bar.js b/testing/src/main/resources/path/foo/bar.js similarity index 100% rename from core/src/test/resources/path/foo/bar.js rename to testing/src/main/resources/path/foo/bar.js diff --git a/core/src/test/resources/path/foo/print/fob.js b/testing/src/main/resources/path/foo/print/fob.js similarity index 100% rename from core/src/test/resources/path/foo/print/fob.js rename to testing/src/main/resources/path/foo/print/fob.js diff --git a/core/src/test/resources/webjars/htmx.org-1.8.4.jar b/testing/src/main/resources/webjars/htmx.org-1.8.4.jar similarity index 100% rename from core/src/test/resources/webjars/htmx.org-1.8.4.jar rename to testing/src/main/resources/webjars/htmx.org-1.8.4.jar diff --git a/core/src/test/resources/webjars/restart__hooks-0.4.9.jar b/testing/src/main/resources/webjars/restart__hooks-0.4.9.jar similarity index 100% rename from core/src/test/resources/webjars/restart__hooks-0.4.9.jar rename to testing/src/main/resources/webjars/restart__hooks-0.4.9.jar diff --git a/testing/src/test/java/io/mvnpm/esbuild/BundlerBuildTest.java b/testing/src/test/java/io/mvnpm/esbuild/BundlerBuildTest.java new file mode 100644 index 00000000..8b5c520b --- /dev/null +++ b/testing/src/test/java/io/mvnpm/esbuild/BundlerBuildTest.java @@ -0,0 +1,67 @@ +package io.mvnpm.esbuild; + +import static io.mvnpm.esbuild.BundlerTestHelper.executeTest; +import static io.mvnpm.esbuild.BundlerTestHelper.getJars; +import static org.junit.jupiter.api.Assertions.*; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import io.mvnpm.esbuild.model.*; +import io.mvnpm.esbuild.model.WebDependency.WebDependencyType; + +public class BundlerBuildTest { + + @Test + public void shouldBundleMvnpm() throws URISyntaxException, IOException { + executeTest(List.of("/mvnpm/stimulus-3.2.1.jar"), WebDependencyType.MVNPM, "application-mvnpm.js", true); + } + + @Test + public void shouldBundleMvnpmSources() throws URISyntaxException, IOException { + executeTest(List.of("/mvnpm/moment-2.29.4-sources.jar"), WebDependencyType.MVNPM, + "application-mvnpm.ts", true); + } + + @Test + public void shouldBundleMvnpmAndCreatePackageJson() throws URISyntaxException, IOException { + executeTest(List.of("/mvnpm/stimulus-3.2.0.jar"), WebDependencyType.MVNPM, "application-mvnpm.js", true); + } + + @Test + public void shouldBundleMvnpmWithoutPackageJson() throws URISyntaxException, IOException { + executeTest(List.of("/mvnpm/polymer-3.5.1.jar"), WebDependencyType.MVNPM, "application-mvnpm-importmap.js", true); + } + + @Test + public void shouldBundle() throws URISyntaxException, IOException { + executeTest(List.of("/webjars/htmx.org-1.8.4.jar"), WebDependencyType.WEBJARS, "application-webjar.js", true); + } + + @Test + public void shouldThrowException() { + assertThrows(BundleException.class, () -> { + executeTest(List.of("/mvnpm/stimulus-3.2.1.jar"), WebDependencyType.MVNPM, "application-error.js", false); + }); + } + + @Test + public void shouldResolveRelativeFolders() throws URISyntaxException, IOException { + // given + final Path root = new File(getClass().getResource("/path/").toURI()).toPath(); + final BundleOptions bundleOptions = BundleOptions.builder().withWorkDir(root) + .addAutoEntryPoint(root, "main", List.of("foo/bar.js")).build(); + + // when + final BundleResult result = Bundler.bundle(bundleOptions, true); + + // then + assertTrue(result.dist().toFile().exists()); + } + +} diff --git a/testing/src/test/java/io/mvnpm/esbuild/BundlerDevTest.java b/testing/src/test/java/io/mvnpm/esbuild/BundlerDevTest.java new file mode 100644 index 00000000..d31b4959 --- /dev/null +++ b/testing/src/test/java/io/mvnpm/esbuild/BundlerDevTest.java @@ -0,0 +1,186 @@ +package io.mvnpm.esbuild; + +import io.mvnpm.esbuild.model.BundleOptions; +import io.mvnpm.esbuild.model.DevResult; +import io.mvnpm.esbuild.model.EsBuildConfig; +import io.mvnpm.esbuild.model.WebDependency.WebDependencyType; +import io.mvnpm.esbuild.script.DevProcess; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import static io.mvnpm.esbuild.BundlerTestHelper.getBundleOptions; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class BundlerDevTest { + + @Test + public void shouldDev() throws URISyntaxException, IOException, InterruptedException { + // given + final BundleOptions options = getBundleOptions(List.of("/mvnpm/stimulus-3.2.1.jar"), + WebDependencyType.MVNPM, + "application-mvnpm.js").withEsConfig(EsBuildConfig.builder().fixedEntryNames().build()).build(); + + // when + AtomicReference bundleException = new AtomicReference<>(); + final DevResult devResult = Bundler.dev(options, true); + + // then + DevProcess process = devResult.process(); + assertTrue(process.isAlive(), "process is alive"); + try (process) { + process.build(); + assertTrue(process.isAlive(), "process is alive"); + final Path app = process.workDir().resolve("application-mvnpm.js"); + assertTrue(Files.exists(app)); + final Path distApp = process.dist().resolve("application-mvnpm.js"); + assertTrue(Files.exists(distApp)); + + // when + Files.writeString(app, "\nalert(\"foo\");", StandardOpenOption.APPEND); + process.build(); + assertTrue(process.isAlive(), "process is alive"); + assertNull(bundleException.get(), "No error during bundling"); + assertTrue(Files.readString(distApp).contains("alert(\"foo\");")); + + } + + assertFalse(process.isAlive()); + } + + @Test + public void shouldMultipleDevInMultipleThreads() throws URISyntaxException, IOException, InterruptedException { + final BundleOptions options = getBundleOptions( + List.of("/mvnpm/stimulus-3.2.1.jar"), + WebDependencyType.MVNPM, + "application-mvnpm.js").withEsConfig( + EsBuildConfig.builder() + .fixedEntryNames() + .define("foo", "\"bar") + .build()) + .build(); + + ExecutorService executor = Executors.newFixedThreadPool(5); + AtomicInteger counter = new AtomicInteger(0); + AtomicReference r = new AtomicReference<>(); + int targetCount = 5; + for (int i = 0; i < targetCount; i++) { + final int idx = i; + executor.submit(() -> { + System.out.println("Running task " + idx); + try (final DevResult result = Bundler.dev(options, true)) { + result.process().build(); + r.set(result); + } catch (BundleException e) { + // good + counter.incrementAndGet(); + System.out.println("good"); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + executor.shutdown(); + executor.awaitTermination(10, TimeUnit.SECONDS); + assertEquals(targetCount, counter.get()); + + if (r.get() != null) { + throw new RuntimeException(r.get().toString()); + } + } + + @Test + public void shouldDevInMultipleThreads() throws URISyntaxException, IOException, InterruptedException { + final BundleOptions options = getBundleOptions( + List.of("/mvnpm/stimulus-3.2.1.jar"), + WebDependencyType.MVNPM, + "application-mvnpm.js").withEsConfig( + EsBuildConfig.builder() + .fixedEntryNames() + .define("foo", "\"bar") + .build()) + .build(); + + ExecutorService executor = Executors.newFixedThreadPool(5); + AtomicInteger counter = new AtomicInteger(0); + AtomicReference r = new AtomicReference<>(); + int targetCount = 5; + + try (final DevResult result = Bundler.dev(options, true)) { + for (int i = 0; i < targetCount; i++) { + final int idx = i; + executor.submit(() -> { + System.out.println("Running task " + idx); + try { + result.process().build(); + r.set(result); + } catch (BundleException e) { + // good + counter.incrementAndGet(); + System.out.println("good"); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + executor.shutdown(); + executor.awaitTermination(10, TimeUnit.SECONDS); + } + assertEquals(targetCount, counter.get()); + + if (r.get() != null) { + throw new RuntimeException(r.get().toString()); + } + } + + @Test + public void shouldDevWithError() throws URISyntaxException, IOException, InterruptedException { + + // given + final BundleOptions options = getBundleOptions(List.of("/mvnpm/stimulus-3.2.1.jar"), + WebDependencyType.MVNPM, + "application-error.js").withEsConfig(EsBuildConfig.builder().fixedEntryNames().build()).build(); + + // when + final DevResult devResult = Bundler.dev(options, true); + + // then + DevProcess process = devResult.process(); + assertTrue(process.isAlive(), "process is alive"); + try (process) { + + assertThrows(BundleException.class, process::build); + assertTrue(process.isAlive(), "process is alive"); + + final Path app = process.workDir().resolve("application-error.js"); + assertTrue(Files.exists(app)); + + // when + Files.writeString(app, "alert(\"foo\");", StandardOpenOption.TRUNCATE_EXISTING); + + // then + process.build(); + final Path distApp = process.dist().resolve("application-error.js"); + assertTrue(Files.exists(distApp)); + assertTrue(Files.readString(distApp).contains("alert(\"foo\");")); + + } + // then + + } + +} diff --git a/testing/src/test/java/io/mvnpm/esbuild/ScriptRunnerTest.java b/testing/src/test/java/io/mvnpm/esbuild/ScriptRunnerTest.java new file mode 100644 index 00000000..9509ae96 --- /dev/null +++ b/testing/src/test/java/io/mvnpm/esbuild/ScriptRunnerTest.java @@ -0,0 +1,21 @@ +package io.mvnpm.esbuild; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.junit.jupiter.api.Test; + +import io.mvnpm.esbuild.model.EsBuildConfigBuilder; +import io.mvnpm.esbuild.script.ScriptRunner; + +public class ScriptRunnerTest { + + @Test + public void shouldExecuteEsBuild() throws IOException { + final EsBuildConfigBuilder esBuildConfig = new EsBuildConfigBuilder(); + esBuildConfig.version(); + Path workingDirectory = Files.createTempDirectory("testBuild"); + new ScriptRunner(workingDirectory, esBuildConfig.build()).build(); + } +} diff --git a/core/src/test/java/io/mvnpm/esbuild/install/WebDepsInstallerTest.java b/testing/src/test/java/io/mvnpm/esbuild/install/WebDepsInstallerTest.java similarity index 100% rename from core/src/test/java/io/mvnpm/esbuild/install/WebDepsInstallerTest.java rename to testing/src/test/java/io/mvnpm/esbuild/install/WebDepsInstallerTest.java diff --git a/core/src/test/java/io/mvnpm/esbuild/model/AutoEntryPointTest.java b/testing/src/test/java/io/mvnpm/esbuild/model/AutoEntryPointTest.java similarity index 100% rename from core/src/test/java/io/mvnpm/esbuild/model/AutoEntryPointTest.java rename to testing/src/test/java/io/mvnpm/esbuild/model/AutoEntryPointTest.java diff --git a/testing/src/test/java/io/mvnpm/esbuild/model/EsBuildConfigToJsonTest.java b/testing/src/test/java/io/mvnpm/esbuild/model/EsBuildConfigToJsonTest.java new file mode 100644 index 00000000..c844b8da --- /dev/null +++ b/testing/src/test/java/io/mvnpm/esbuild/model/EsBuildConfigToJsonTest.java @@ -0,0 +1,115 @@ +package io.mvnpm.esbuild.model; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class EsBuildConfigToJsonTest { + + @Test + public void shouldOutputFlagsAsJson() throws Exception { + final EsBuildConfigBuilder esBuildConfig = new EsBuildConfigBuilder(); + esBuildConfig.bundle(true); + esBuildConfig.format(EsBuildConfig.Format.ESM); + + final String json = esBuildConfig.build().toJson(); + + JsonNode node = new ObjectMapper().readTree(json); + assertTrue(node.get("bundle").asBoolean()); + assertEquals("esm", node.get("format").asText()); + } + + @Test + public void shouldOutputLoaderFlagsAsJson() throws Exception { + final EsBuildConfigBuilder esBuildConfig = new EsBuildConfigBuilder(); + esBuildConfig.loader(Map.of(".js", EsBuildConfig.Loader.JSX, ".css", EsBuildConfig.Loader.LOCAL_CSS)); + + final String json = esBuildConfig.build().toJson(); + + JsonNode node = new ObjectMapper().readTree(json); + JsonNode loaderNode = node.get("loader"); + assertEquals("jsx", loaderNode.get(".js").asText()); + assertEquals("local-css", loaderNode.get(".css").asText()); + } + + @Test + public void shouldOutputPublicPathFlagAsJson() throws Exception { + final EsBuildConfigBuilder esBuildConfig = new EsBuildConfigBuilder(); + esBuildConfig.publicPath("https://www.example.com/v1"); + + final String json = esBuildConfig.build().toJson(); + + JsonNode node = new ObjectMapper().readTree(json); + assertEquals("https://www.example.com/v1", node.get("publicPath").asText()); + } + + @Test + public void shouldOutputStandardFlagsAsJson() throws Exception { + final EsBuildConfig esBuildConfig = EsBuildConfig.builder().bundle() + .entryPoint(new String[] { "main.js", "bundle.js" }).outDir("/tmp").minify().build(); + + final String json = esBuildConfig.toJson(); + + JsonNode node = new ObjectMapper().readTree(json); + assertTrue(node.get("bundle").asBoolean()); + assertTrue(node.get("minify").asBoolean()); + assertEquals("/tmp", node.get("outdir").asText()); + + JsonNode entryPoints = node.get("entryPoints"); + assertTrue(entryPoints.isArray()); + assertTrue(StreamSupport.stream(entryPoints.spliterator(), false) + .map(JsonNode::asText) + .collect(Collectors.toSet()) + .containsAll(List.of("main.js", "bundle.js"))); + } + + @Test + public void shouldExternalAsJson() throws Exception { + final EsBuildConfigBuilder esBuildConfig = new EsBuildConfigBuilder(); + esBuildConfig.addExternal("*.png"); + esBuildConfig.addExternal("/images/*"); + + final String json = esBuildConfig.build().toJson(); + + JsonNode node = new ObjectMapper().readTree(json); + JsonNode externalNode = node.get("external"); + assertTrue(externalNode.isArray()); + assertTrue(StreamSupport.stream(externalNode.spliterator(), false) + .map(JsonNode::asText) + .collect(Collectors.toSet()) + .containsAll(List.of("*.png", "/images/*"))); + } + + @Test + public void shouldAddChunkNamesAsJson() throws Exception { + final EsBuildConfigBuilder esBuildConfig = new EsBuildConfigBuilder(); + esBuildConfig.chunkNames("chunks/[name]-[hash]"); + + final String json = esBuildConfig.build().toJson(); + + JsonNode node = new ObjectMapper().readTree(json); + assertEquals("chunks/[name]-[hash]", node.get("chunkNames").asText()); + } + + @Test + public void shouldNotChangeCaseInDefineJson() throws Exception { + final EsBuildConfigBuilder esBuildConfig = new EsBuildConfigBuilder(); + esBuildConfig.define.put("FOO", "BAR"); + esBuildConfig.define.put("foo", "bar"); + + final String json = esBuildConfig.build().toJson(); + + JsonNode node = new ObjectMapper().readTree(json); + JsonNode defineNode = node.get("define"); + assertEquals("bar", defineNode.get("foo").asText()); + assertEquals("BAR", defineNode.get("FOO").asText()); // values are lowercased + } +} diff --git a/core/src/test/java/io/mvnpm/esbuild/model/EsBuildConfigTest.java b/testing/src/test/java/io/mvnpm/esbuild/model/EsBuildConfigToParamTest.java similarity index 98% rename from core/src/test/java/io/mvnpm/esbuild/model/EsBuildConfigTest.java rename to testing/src/test/java/io/mvnpm/esbuild/model/EsBuildConfigToParamTest.java index a1ca4336..383fcff0 100644 --- a/core/src/test/java/io/mvnpm/esbuild/model/EsBuildConfigTest.java +++ b/testing/src/test/java/io/mvnpm/esbuild/model/EsBuildConfigToParamTest.java @@ -8,7 +8,7 @@ import org.junit.jupiter.api.Test; -public class EsBuildConfigTest { +public class EsBuildConfigToParamTest { @Test public void shouldOutputFlags() { diff --git a/bundle-original/src/test/java/io/mvnpm/esbuild/resolve/BundleTester.java b/testing/src/test/java/io/mvnpm/esbuild/resolve/BundleTester.java similarity index 100% rename from bundle-original/src/test/java/io/mvnpm/esbuild/resolve/BundleTester.java rename to testing/src/test/java/io/mvnpm/esbuild/resolve/BundleTester.java diff --git a/core/src/test/java/io/mvnpm/esbuild/resolve/DownloadResolverTest.java b/testing/src/test/java/io/mvnpm/esbuild/resolve/DownloadResolverTest.java similarity index 100% rename from core/src/test/java/io/mvnpm/esbuild/resolve/DownloadResolverTest.java rename to testing/src/test/java/io/mvnpm/esbuild/resolve/DownloadResolverTest.java diff --git a/core/src/test/java/io/mvnpm/esbuild/util/ArchivesTest.java b/testing/src/test/java/io/mvnpm/esbuild/util/ArchivesTest.java similarity index 100% rename from core/src/test/java/io/mvnpm/esbuild/util/ArchivesTest.java rename to testing/src/test/java/io/mvnpm/esbuild/util/ArchivesTest.java diff --git a/core/src/test/java/io/mvnpm/esbuild/util/PackageJsonTest.java b/testing/src/test/java/io/mvnpm/esbuild/util/PackageJsonTest.java similarity index 100% rename from core/src/test/java/io/mvnpm/esbuild/util/PackageJsonTest.java rename to testing/src/test/java/io/mvnpm/esbuild/util/PackageJsonTest.java diff --git a/core/src/test/java/io/mvnpm/esbuild/util/PathUtilsTest.java b/testing/src/test/java/io/mvnpm/esbuild/util/PathUtilsTest.java similarity index 100% rename from core/src/test/java/io/mvnpm/esbuild/util/PathUtilsTest.java rename to testing/src/test/java/io/mvnpm/esbuild/util/PathUtilsTest.java