Compare commits
1 Commits
main
...
bundle_jar
Author | SHA1 | Date | |
---|---|---|---|
|
7d8ae040fd |
@ -17,15 +17,20 @@ import { isInside } from "../../tools/isInside";
|
|||||||
import child_process from "child_process";
|
import child_process from "child_process";
|
||||||
import { rmSync } from "../../tools/fs.rmSync";
|
import { rmSync } from "../../tools/fs.rmSync";
|
||||||
import { writeMetaInfKeycloakThemes } from "../../shared/metaInfKeycloakThemes";
|
import { writeMetaInfKeycloakThemes } from "../../shared/metaInfKeycloakThemes";
|
||||||
|
import {
|
||||||
|
bundleExtensionsIntoJar,
|
||||||
|
type BuildContextLike as BuildContextLike_bundleExtensionsIntoJar
|
||||||
|
} from "./bundleExtensionsIntoJar";
|
||||||
|
|
||||||
export type BuildContextLike = BuildContextLike_generatePom & {
|
export type BuildContextLike = BuildContextLike_generatePom &
|
||||||
keycloakifyBuildDirPath: string;
|
BuildContextLike_bundleExtensionsIntoJar & {
|
||||||
themeNames: string[];
|
keycloakifyBuildDirPath: string;
|
||||||
artifactId: string;
|
themeNames: string[];
|
||||||
themeVersion: string;
|
artifactId: string;
|
||||||
cacheDirPath: string;
|
themeVersion: string;
|
||||||
implementedThemeTypes: BuildContext["implementedThemeTypes"];
|
cacheDirPath: string;
|
||||||
};
|
implementedThemeTypes: BuildContext["implementedThemeTypes"];
|
||||||
|
};
|
||||||
|
|
||||||
assert<BuildContext extends BuildContextLike ? true : false>();
|
assert<BuildContext extends BuildContextLike ? true : false>();
|
||||||
|
|
||||||
@ -234,12 +239,19 @@ export async function buildJar(params: {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const jarFilePath_generatedByMaven = pathJoin(
|
||||||
|
keycloakifyBuildCacheDirPath,
|
||||||
|
"target",
|
||||||
|
`${buildContext.artifactId}-${buildContext.themeVersion}.jar`
|
||||||
|
);
|
||||||
|
|
||||||
|
await bundleExtensionsIntoJar({
|
||||||
|
buildContext,
|
||||||
|
jarFilePath: jarFilePath_generatedByMaven
|
||||||
|
});
|
||||||
|
|
||||||
await fs.rename(
|
await fs.rename(
|
||||||
pathJoin(
|
jarFilePath_generatedByMaven,
|
||||||
keycloakifyBuildCacheDirPath,
|
|
||||||
"target",
|
|
||||||
`${buildContext.artifactId}-${buildContext.themeVersion}.jar`
|
|
||||||
),
|
|
||||||
pathJoin(buildContext.keycloakifyBuildDirPath, jarFileBasename)
|
pathJoin(buildContext.keycloakifyBuildDirPath, jarFileBasename)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
137
src/bin/keycloakify/buildJars/bundleExtensionsIntoJar.ts
Normal file
137
src/bin/keycloakify/buildJars/bundleExtensionsIntoJar.ts
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
import { downloadAndExtractArchive } from "../../tools/downloadAndExtractArchive";
|
||||||
|
import { assert } from "tsafe/assert";
|
||||||
|
import type { BuildContext } from "../../shared/buildContext";
|
||||||
|
import { transformCodebase } from "../../tools/transformCodebase";
|
||||||
|
import { join as pathJoin, basename as pathBasename, sep as pathSep } from "path";
|
||||||
|
import { rm } from "../../tools/fs.rm";
|
||||||
|
import { extractArchive } from "../../tools/extractArchive";
|
||||||
|
import * as crypto from "crypto";
|
||||||
|
|
||||||
|
export type BuildContextLike = {
|
||||||
|
cacheDirPath: string;
|
||||||
|
fetchOptions: BuildContext["fetchOptions"];
|
||||||
|
extensionJars: BuildContext["extensionJars"];
|
||||||
|
};
|
||||||
|
|
||||||
|
assert<BuildContext extends BuildContextLike ? true : false>();
|
||||||
|
|
||||||
|
export async function bundleExtensionsIntoJar(params: {
|
||||||
|
jarFilePath: string;
|
||||||
|
buildContext: BuildContextLike;
|
||||||
|
}): Promise<void> {
|
||||||
|
const { jarFilePath, buildContext } = params;
|
||||||
|
|
||||||
|
if (buildContext.extensionJars.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mergeDirPath = pathJoin(
|
||||||
|
buildContext.cacheDirPath,
|
||||||
|
`merge_${pathBasename(jarFilePath).replace(/\.jar$/, "")}_${crypto
|
||||||
|
.createHash("sha256")
|
||||||
|
.update(jarFilePath)
|
||||||
|
.digest("hex")
|
||||||
|
.substring(0, 5)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
await extractArchive({
|
||||||
|
archiveFilePath: jarFilePath,
|
||||||
|
onArchiveFile: async ({ relativeFilePathInArchive, writeFile }) =>
|
||||||
|
writeFile({
|
||||||
|
filePath: pathJoin(mergeDirPath, relativeFilePathInArchive)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const extensionJar of buildContext.extensionJars) {
|
||||||
|
const transformSourceCode = (params: {
|
||||||
|
fileRelativePath: string;
|
||||||
|
sourceCode: Buffer;
|
||||||
|
}): { modifiedSourceCode: Buffer } | undefined => {
|
||||||
|
const { fileRelativePath } = params;
|
||||||
|
|
||||||
|
if (!fileRelativePath.startsWith(`META-INF${pathSep}`)) {
|
||||||
|
for (const ext of [".DSA", ".SF", ".RSA"]) {
|
||||||
|
if (fileRelativePath.endsWith(ext)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (extensionJar.type) {
|
||||||
|
case "path":
|
||||||
|
await extractArchive({
|
||||||
|
archiveFilePath: extensionJar.path,
|
||||||
|
onArchiveFile: async ({
|
||||||
|
relativeFilePathInArchive,
|
||||||
|
writeFile,
|
||||||
|
readFile
|
||||||
|
}) => {
|
||||||
|
const transformResult = transformSourceCode({
|
||||||
|
fileRelativePath: relativeFilePathInArchive,
|
||||||
|
sourceCode: await readFile()
|
||||||
|
});
|
||||||
|
|
||||||
|
if (transformResult === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await writeFile({
|
||||||
|
filePath: pathJoin(mergeDirPath, relativeFilePathInArchive),
|
||||||
|
modifiedData: transformResult.modifiedSourceCode
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "url": {
|
||||||
|
const { extractedDirPath } = await downloadAndExtractArchive({
|
||||||
|
url: extensionJar.url,
|
||||||
|
cacheDirPath: buildContext.cacheDirPath,
|
||||||
|
fetchOptions: buildContext.fetchOptions,
|
||||||
|
uniqueIdOfOnArchiveFile: "noOp",
|
||||||
|
onArchiveFile: async ({ fileRelativePath, writeFile }) =>
|
||||||
|
writeFile({ fileRelativePath })
|
||||||
|
});
|
||||||
|
transformCodebase({
|
||||||
|
srcDirPath: extractedDirPath,
|
||||||
|
destDirPath: mergeDirPath,
|
||||||
|
transformSourceCode
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
transformCodebase({
|
||||||
|
srcDirPath: extractedDirPath,
|
||||||
|
destDirPath: mergeDirPath,
|
||||||
|
transformSourceCode: ({ fileRelativePath, sourceCode }) => {
|
||||||
|
if (fileRelativePath === pathJoin("META-INF", "MANIFEST.MF")) {
|
||||||
|
const sourceCodeStr = sourceCode.toString("utf8");
|
||||||
|
|
||||||
|
const lines = sourceCodeStr.split(/\r?\n/);
|
||||||
|
|
||||||
|
console.log(lines);
|
||||||
|
|
||||||
|
return {
|
||||||
|
modifiedSourceCode: Buffer.concat([
|
||||||
|
sourceCode,
|
||||||
|
Buffer.from(
|
||||||
|
`Class-Path: ${pathBasename(userProvidedJarFilePathOrUrl)}\n`
|
||||||
|
)
|
||||||
|
])
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Acctually build new jar
|
||||||
|
|
||||||
|
await rm(mergeDirPath, { recursive: true, force: true });
|
||||||
|
}
|
@ -314,8 +314,7 @@ export async function generateResourcesForMainTheme(params: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { extractedDirPath } = await downloadAndExtractArchive({
|
const { extractedDirPath } = await downloadAndExtractArchive({
|
||||||
urlOrPath:
|
url: "https://repo1.maven.org/maven2/org/keycloak/keycloak-account-ui/25.0.1/keycloak-account-ui-25.0.1.jar",
|
||||||
"https://repo1.maven.org/maven2/org/keycloak/keycloak-account-ui/25.0.1/keycloak-account-ui-25.0.1.jar",
|
|
||||||
cacheDirPath: buildContext.cacheDirPath,
|
cacheDirPath: buildContext.cacheDirPath,
|
||||||
fetchOptions: buildContext.fetchOptions,
|
fetchOptions: buildContext.fetchOptions,
|
||||||
uniqueIdOfOnArchiveFile: "bring_in_account_v3_i18n_messages",
|
uniqueIdOfOnArchiveFile: "bring_in_account_v3_i18n_messages",
|
||||||
|
@ -26,6 +26,8 @@ import { type ThemeType } from "./constants";
|
|||||||
import { id } from "tsafe/id";
|
import { id } from "tsafe/id";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { getProxyFetchOptions, type ProxyFetchOptions } from "../tools/fetchProxyOptions";
|
import { getProxyFetchOptions, type ProxyFetchOptions } from "../tools/fetchProxyOptions";
|
||||||
|
import { removeDuplicates } from "evt/tools/reducers/removeDuplicates";
|
||||||
|
import { same } from "evt/tools/inDepth/same";
|
||||||
|
|
||||||
export type BuildContext = {
|
export type BuildContext = {
|
||||||
themeVersion: string;
|
themeVersion: string;
|
||||||
@ -61,6 +63,7 @@ export type BuildContext = {
|
|||||||
keycloakVersionRange: KeycloakVersionRange;
|
keycloakVersionRange: KeycloakVersionRange;
|
||||||
jarFileBasename: string;
|
jarFileBasename: string;
|
||||||
}[];
|
}[];
|
||||||
|
extensionJars: ({ type: "path"; path: string } | { type: "url"; url: string })[];
|
||||||
startKeycloakOptions: {
|
startKeycloakOptions: {
|
||||||
dockerImage:
|
dockerImage:
|
||||||
| {
|
| {
|
||||||
@ -88,6 +91,7 @@ export type BuildOptions = {
|
|||||||
loginThemeResourcesFromKeycloakVersion?: string;
|
loginThemeResourcesFromKeycloakVersion?: string;
|
||||||
keycloakifyBuildDirPath?: string;
|
keycloakifyBuildDirPath?: string;
|
||||||
kcContextExclusionsFtl?: string;
|
kcContextExclusionsFtl?: string;
|
||||||
|
extensionJars?: string[];
|
||||||
startKeycloakOptions?: {
|
startKeycloakOptions?: {
|
||||||
dockerImage?: string;
|
dockerImage?: string;
|
||||||
dockerExtraArgs?: string[];
|
dockerExtraArgs?: string[];
|
||||||
@ -360,6 +364,7 @@ export function getBuildContext(params: {
|
|||||||
loginThemeResourcesFromKeycloakVersion: z.string().optional(),
|
loginThemeResourcesFromKeycloakVersion: z.string().optional(),
|
||||||
keycloakifyBuildDirPath: z.string().optional(),
|
keycloakifyBuildDirPath: z.string().optional(),
|
||||||
kcContextExclusionsFtl: z.string().optional(),
|
kcContextExclusionsFtl: z.string().optional(),
|
||||||
|
extensionJars: z.array(z.string()).optional(),
|
||||||
startKeycloakOptions: zStartKeycloakOptions.optional()
|
startKeycloakOptions: zStartKeycloakOptions.optional()
|
||||||
}),
|
}),
|
||||||
zAccountThemeImplAndKeycloakVersionTargets
|
zAccountThemeImplAndKeycloakVersionTargets
|
||||||
@ -520,6 +525,36 @@ export function getBuildContext(params: {
|
|||||||
return pathJoin(projectDirPath, resolvedViteConfig.buildDir);
|
return pathJoin(projectDirPath, resolvedViteConfig.buildDir);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
const buildForKeycloakMajorVersionNumber = (() => {
|
||||||
|
const envValue = process.env[BUILD_FOR_KEYCLOAK_MAJOR_VERSION_ENV_NAME];
|
||||||
|
|
||||||
|
if (envValue === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const major = parseInt(envValue);
|
||||||
|
|
||||||
|
assert(!isNaN(major));
|
||||||
|
|
||||||
|
return major;
|
||||||
|
})();
|
||||||
|
|
||||||
|
function urlOrPathToDiscriminatingWrapper(
|
||||||
|
urlOrPath: string
|
||||||
|
): { type: "url"; url: string } | { type: "path"; path: string } {
|
||||||
|
if (/^https?:\/\//.test(urlOrPath)) {
|
||||||
|
return { type: "url", url: urlOrPath };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "path",
|
||||||
|
path: getAbsoluteAndInOsFormatPath({
|
||||||
|
pathIsh: urlOrPath,
|
||||||
|
cwd: projectDirPath
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
bundler,
|
bundler,
|
||||||
packageJsonFilePath,
|
packageJsonFilePath,
|
||||||
@ -717,21 +752,6 @@ export function getBuildContext(params: {
|
|||||||
`keycloak-theme-for-kc-${range}.jar`;
|
`keycloak-theme-for-kc-${range}.jar`;
|
||||||
|
|
||||||
build_for_specific_keycloak_major_version: {
|
build_for_specific_keycloak_major_version: {
|
||||||
const buildForKeycloakMajorVersionNumber = (() => {
|
|
||||||
const envValue =
|
|
||||||
process.env[BUILD_FOR_KEYCLOAK_MAJOR_VERSION_ENV_NAME];
|
|
||||||
|
|
||||||
if (envValue === undefined) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const major = parseInt(envValue);
|
|
||||||
|
|
||||||
assert(!isNaN(major));
|
|
||||||
|
|
||||||
return major;
|
|
||||||
})();
|
|
||||||
|
|
||||||
if (buildForKeycloakMajorVersionNumber === undefined) {
|
if (buildForKeycloakMajorVersionNumber === undefined) {
|
||||||
break build_for_specific_keycloak_major_version;
|
break build_for_specific_keycloak_major_version;
|
||||||
}
|
}
|
||||||
@ -931,6 +951,10 @@ export function getBuildContext(params: {
|
|||||||
|
|
||||||
return jarTargets;
|
return jarTargets;
|
||||||
})(),
|
})(),
|
||||||
|
extensionJars: (buildForKeycloakMajorVersionNumber !== undefined
|
||||||
|
? []
|
||||||
|
: buildOptions.extensionJars ?? []
|
||||||
|
).map(urlOrPath => urlOrPathToDiscriminatingWrapper(urlOrPath)),
|
||||||
startKeycloakOptions: {
|
startKeycloakOptions: {
|
||||||
dockerImage: (() => {
|
dockerImage: (() => {
|
||||||
if (buildOptions.startKeycloakOptions?.dockerImage === undefined) {
|
if (buildOptions.startKeycloakOptions?.dockerImage === undefined) {
|
||||||
@ -949,21 +973,14 @@ export function getBuildContext(params: {
|
|||||||
})(),
|
})(),
|
||||||
dockerExtraArgs: buildOptions.startKeycloakOptions?.dockerExtraArgs ?? [],
|
dockerExtraArgs: buildOptions.startKeycloakOptions?.dockerExtraArgs ?? [],
|
||||||
keycloakExtraArgs: buildOptions.startKeycloakOptions?.keycloakExtraArgs ?? [],
|
keycloakExtraArgs: buildOptions.startKeycloakOptions?.keycloakExtraArgs ?? [],
|
||||||
extensionJars: (buildOptions.startKeycloakOptions?.extensionJars ?? []).map(
|
extensionJars: [
|
||||||
urlOrPath => {
|
...(buildForKeycloakMajorVersionNumber !== undefined
|
||||||
if (/^https?:\/\//.test(urlOrPath)) {
|
? buildOptions.extensionJars ?? []
|
||||||
return { type: "url", url: urlOrPath };
|
: []),
|
||||||
}
|
...(buildOptions.startKeycloakOptions?.extensionJars ?? [])
|
||||||
|
]
|
||||||
return {
|
.map(urlOrPath => urlOrPathToDiscriminatingWrapper(urlOrPath))
|
||||||
type: "path",
|
.reduce(...removeDuplicates<BuildContext["extensionJars"][number]>(same)),
|
||||||
path: getAbsoluteAndInOsFormatPath({
|
|
||||||
pathIsh: urlOrPath,
|
|
||||||
cwd: projectDirPath
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
),
|
|
||||||
realmJsonFilePath:
|
realmJsonFilePath:
|
||||||
buildOptions.startKeycloakOptions?.realmJsonFilePath === undefined
|
buildOptions.startKeycloakOptions?.realmJsonFilePath === undefined
|
||||||
? undefined
|
? undefined
|
||||||
|
@ -21,7 +21,7 @@ export async function downloadKeycloakDefaultTheme(params: {
|
|||||||
let kcNodeModulesKeepFilePaths_lastAccountV1: Set<string> | undefined = undefined;
|
let kcNodeModulesKeepFilePaths_lastAccountV1: Set<string> | undefined = undefined;
|
||||||
|
|
||||||
const { extractedDirPath } = await downloadAndExtractArchive({
|
const { extractedDirPath } = await downloadAndExtractArchive({
|
||||||
urlOrPath: `https://repo1.maven.org/maven2/org/keycloak/keycloak-themes/${keycloakVersion}/keycloak-themes-${keycloakVersion}.jar`,
|
url: `https://repo1.maven.org/maven2/org/keycloak/keycloak-themes/${keycloakVersion}/keycloak-themes-${keycloakVersion}.jar`,
|
||||||
cacheDirPath: buildContext.cacheDirPath,
|
cacheDirPath: buildContext.cacheDirPath,
|
||||||
fetchOptions: buildContext.fetchOptions,
|
fetchOptions: buildContext.fetchOptions,
|
||||||
uniqueIdOfOnArchiveFile: "downloadKeycloakDefaultTheme",
|
uniqueIdOfOnArchiveFile: "downloadKeycloakDefaultTheme",
|
||||||
|
@ -200,7 +200,7 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
const { archiveFilePath } = await downloadAndExtractArchive({
|
const { archiveFilePath } = await downloadAndExtractArchive({
|
||||||
cacheDirPath: buildContext.cacheDirPath,
|
cacheDirPath: buildContext.cacheDirPath,
|
||||||
fetchOptions: buildContext.fetchOptions,
|
fetchOptions: buildContext.fetchOptions,
|
||||||
urlOrPath: extensionJar.url,
|
url: extensionJar.url,
|
||||||
uniqueIdOfOnArchiveFile: "no extraction",
|
uniqueIdOfOnArchiveFile: "no extraction",
|
||||||
onArchiveFile: async () => {}
|
onArchiveFile: async () => {}
|
||||||
});
|
});
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
import fetch, { type FetchOptions } from "make-fetch-happen";
|
import fetch, { type FetchOptions } from "make-fetch-happen";
|
||||||
import { mkdir, unlink, writeFile, readdir, readFile } from "fs/promises";
|
import { mkdir, unlink, writeFile, readdir, readFile } from "fs/promises";
|
||||||
import { dirname as pathDirname, join as pathJoin, basename as pathBasename } from "path";
|
import { dirname as pathDirname, join as pathJoin } from "path";
|
||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
import { extractArchive } from "./extractArchive";
|
import { extractArchive } from "./extractArchive";
|
||||||
import { existsAsync } from "./fs.existsAsync";
|
import { existsAsync } from "./fs.existsAsync";
|
||||||
import * as crypto from "crypto";
|
import * as crypto from "crypto";
|
||||||
import { rm } from "./fs.rm";
|
import { rm } from "./fs.rm";
|
||||||
import * as fsPr from "fs/promises";
|
|
||||||
|
|
||||||
export async function downloadAndExtractArchive(params: {
|
export async function downloadAndExtractArchive(params: {
|
||||||
urlOrPath: string;
|
url: string;
|
||||||
uniqueIdOfOnArchiveFile: string;
|
uniqueIdOfOnArchiveFile: string;
|
||||||
onArchiveFile: (params: {
|
onArchiveFile: (params: {
|
||||||
fileRelativePath: string;
|
fileRelativePath: string;
|
||||||
@ -22,33 +21,16 @@ export async function downloadAndExtractArchive(params: {
|
|||||||
cacheDirPath: string;
|
cacheDirPath: string;
|
||||||
fetchOptions: FetchOptions | undefined;
|
fetchOptions: FetchOptions | undefined;
|
||||||
}): Promise<{ extractedDirPath: string; archiveFilePath: string }> {
|
}): Promise<{ extractedDirPath: string; archiveFilePath: string }> {
|
||||||
const {
|
const { url, uniqueIdOfOnArchiveFile, onArchiveFile, cacheDirPath, fetchOptions } =
|
||||||
urlOrPath,
|
params;
|
||||||
uniqueIdOfOnArchiveFile,
|
|
||||||
onArchiveFile,
|
|
||||||
cacheDirPath,
|
|
||||||
fetchOptions
|
|
||||||
} = params;
|
|
||||||
|
|
||||||
const isUrl = /^https?:\/\//.test(urlOrPath);
|
const archiveFileBasename = url.split("?")[0].split("/").reverse()[0];
|
||||||
|
|
||||||
const archiveFileBasename = isUrl
|
|
||||||
? urlOrPath.split("?")[0].split("/").reverse()[0]
|
|
||||||
: pathBasename(urlOrPath);
|
|
||||||
|
|
||||||
const archiveFilePath = pathJoin(cacheDirPath, archiveFileBasename);
|
const archiveFilePath = pathJoin(cacheDirPath, archiveFileBasename);
|
||||||
|
|
||||||
download: {
|
download: {
|
||||||
await mkdir(pathDirname(archiveFilePath), { recursive: true });
|
await mkdir(pathDirname(archiveFilePath), { recursive: true });
|
||||||
|
|
||||||
if (!isUrl) {
|
|
||||||
await fsPr.copyFile(urlOrPath, archiveFilePath);
|
|
||||||
|
|
||||||
break download;
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = urlOrPath;
|
|
||||||
|
|
||||||
if (await existsAsync(archiveFilePath)) {
|
if (await existsAsync(archiveFilePath)) {
|
||||||
const isDownloaded = await SuccessTracker.getIsDownloaded({
|
const isDownloaded = await SuccessTracker.getIsDownloaded({
|
||||||
cacheDirPath,
|
cacheDirPath,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user