2022-08-10 05:56:27 +00:00
|
|
|
import { lang, query as q } from 'good-enough-parser';
|
|
|
|
import { logger } from '../../../logger';
|
|
|
|
import { regEx } from '../../../util/regex';
|
|
|
|
import { parseUrl } from '../../../util/url';
|
2022-02-15 05:12:30 +00:00
|
|
|
import { MavenDatasource } from '../../datasource/maven';
|
|
|
|
import { SbtPackageDatasource } from '../../datasource/sbt-package';
|
|
|
|
import {
|
2022-08-10 05:56:27 +00:00
|
|
|
SBT_PLUGINS_REPO,
|
2022-02-15 05:12:30 +00:00
|
|
|
SbtPluginDatasource,
|
|
|
|
} from '../../datasource/sbt-plugin';
|
2022-08-10 05:56:27 +00:00
|
|
|
import { MAVEN_REPO } from '../gradle/common';
|
2021-03-02 20:44:55 +00:00
|
|
|
import type { PackageDependency, PackageFile } from '../types';
|
2022-08-10 05:56:27 +00:00
|
|
|
import { normalizeScalaVersion } from './util';
|
2019-05-01 06:40:35 +00:00
|
|
|
|
2022-08-10 05:56:27 +00:00
|
|
|
type Vars = Record<string, string>;
|
2019-05-01 06:40:35 +00:00
|
|
|
|
2022-08-10 05:56:27 +00:00
|
|
|
interface Ctx {
|
|
|
|
vars: Vars;
|
|
|
|
deps: PackageDependency[];
|
|
|
|
registryUrls: string[];
|
2019-05-01 06:40:35 +00:00
|
|
|
|
2022-08-10 05:56:27 +00:00
|
|
|
scalaVersion?: string;
|
|
|
|
packageFileVersion?: string;
|
2019-05-01 06:40:35 +00:00
|
|
|
|
2022-08-10 05:56:27 +00:00
|
|
|
groupId?: string;
|
|
|
|
artifactId?: string;
|
|
|
|
currentValue?: string;
|
2019-05-01 06:40:35 +00:00
|
|
|
|
2022-08-10 05:56:27 +00:00
|
|
|
currentVarName?: string;
|
|
|
|
depType?: string;
|
|
|
|
useScalaVersion?: boolean;
|
|
|
|
groupName?: string;
|
|
|
|
}
|
2019-05-01 06:40:35 +00:00
|
|
|
|
2022-08-10 05:56:27 +00:00
|
|
|
const scala = lang.createLang('scala');
|
|
|
|
|
|
|
|
const scalaVersionMatch = q
|
|
|
|
.sym<Ctx>('scalaVersion')
|
|
|
|
.op(':=')
|
|
|
|
.alt(
|
|
|
|
q.str<Ctx>((ctx, { value: scalaVersion }) => ({ ...ctx, scalaVersion })),
|
|
|
|
q.sym<Ctx>((ctx, { value: varName }) => {
|
|
|
|
const scalaVersion = ctx.vars[varName];
|
|
|
|
if (scalaVersion) {
|
|
|
|
ctx.scalaVersion = scalaVersion;
|
|
|
|
}
|
|
|
|
return ctx;
|
|
|
|
})
|
|
|
|
)
|
|
|
|
.handler((ctx) => {
|
|
|
|
if (ctx.scalaVersion) {
|
|
|
|
const dep: PackageDependency = {
|
|
|
|
datasource: MavenDatasource.id,
|
|
|
|
depName: 'scala',
|
|
|
|
packageName: 'org.scala-lang:scala-library',
|
|
|
|
currentValue: ctx.scalaVersion,
|
|
|
|
separateMinorPatch: true,
|
|
|
|
};
|
|
|
|
ctx.scalaVersion = normalizeScalaVersion(ctx.scalaVersion);
|
|
|
|
ctx.deps.push(dep);
|
|
|
|
}
|
|
|
|
return ctx;
|
|
|
|
});
|
|
|
|
|
|
|
|
const packageFileVersionMatch = q
|
|
|
|
.sym<Ctx>('version')
|
|
|
|
.op(':=')
|
|
|
|
.alt(
|
|
|
|
q.str<Ctx>((ctx, { value: packageFileVersion }) => ({
|
|
|
|
...ctx,
|
|
|
|
packageFileVersion,
|
|
|
|
})),
|
|
|
|
q.sym<Ctx>((ctx, { value: varName }) => {
|
|
|
|
const packageFileVersion = ctx.vars[varName];
|
|
|
|
if (packageFileVersion) {
|
|
|
|
ctx.packageFileVersion = packageFileVersion;
|
|
|
|
}
|
|
|
|
return ctx;
|
|
|
|
})
|
2022-01-15 14:47:37 +00:00
|
|
|
);
|
2019-11-23 20:44:55 +00:00
|
|
|
|
2022-08-10 05:56:27 +00:00
|
|
|
const variableNameMatch = q.sym<Ctx>((ctx, { value: varName }) => ({
|
|
|
|
...ctx,
|
|
|
|
currentVarName: varName,
|
|
|
|
}));
|
|
|
|
|
|
|
|
const variableValueMatch = q.str<Ctx>((ctx, { value }) => {
|
|
|
|
ctx.vars[ctx.currentVarName!] = value;
|
|
|
|
delete ctx.currentVarName;
|
|
|
|
return ctx;
|
|
|
|
});
|
|
|
|
|
|
|
|
const assignmentMatch = q.sym<Ctx>('val').join(variableNameMatch).op('=');
|
|
|
|
|
|
|
|
const variableDefinitionMatch = q
|
|
|
|
.alt(
|
|
|
|
q.sym<Ctx>('lazy').join(assignmentMatch),
|
|
|
|
assignmentMatch,
|
|
|
|
variableNameMatch.op(':=')
|
|
|
|
)
|
|
|
|
.join(variableValueMatch);
|
|
|
|
|
|
|
|
const groupIdMatch = q.alt<Ctx>(
|
|
|
|
q.sym<Ctx>((ctx, { value: varName }) => {
|
|
|
|
const currentGroupId = ctx.vars[varName];
|
|
|
|
if (currentGroupId) {
|
|
|
|
ctx.groupId = currentGroupId;
|
2020-03-17 11:15:22 +00:00
|
|
|
}
|
2022-08-10 05:56:27 +00:00
|
|
|
return ctx;
|
|
|
|
}),
|
|
|
|
q.str<Ctx>((ctx, { value: groupId }) => ({ ...ctx, groupId }))
|
|
|
|
);
|
|
|
|
|
|
|
|
const artifactIdMatch = q.alt<Ctx>(
|
|
|
|
q.sym<Ctx>((ctx, { value: varName }) => {
|
|
|
|
const artifactId = ctx.vars[varName];
|
|
|
|
if (artifactId) {
|
|
|
|
ctx.artifactId = artifactId;
|
2020-03-17 11:15:22 +00:00
|
|
|
}
|
2022-08-10 05:56:27 +00:00
|
|
|
return ctx;
|
|
|
|
}),
|
|
|
|
q.str<Ctx>((ctx, { value: artifactId }) => ({ ...ctx, artifactId }))
|
|
|
|
);
|
|
|
|
|
|
|
|
const versionMatch = q.alt<Ctx>(
|
|
|
|
q.sym<Ctx>((ctx, { value: varName }) => {
|
|
|
|
const currentValue = ctx.vars[varName];
|
|
|
|
if (currentValue) {
|
|
|
|
ctx.currentValue = currentValue;
|
|
|
|
ctx.groupName = varName;
|
|
|
|
}
|
|
|
|
return ctx;
|
|
|
|
}),
|
|
|
|
q.str<Ctx>((ctx, { value: currentValue }) => ({ ...ctx, currentValue }))
|
|
|
|
);
|
|
|
|
|
|
|
|
const simpleDependencyMatch = groupIdMatch
|
|
|
|
.op('%')
|
|
|
|
.join(artifactIdMatch)
|
|
|
|
.op('%')
|
|
|
|
.join(versionMatch);
|
|
|
|
|
|
|
|
const versionedDependencyMatch = groupIdMatch
|
|
|
|
.op('%%')
|
|
|
|
.join(artifactIdMatch)
|
|
|
|
.handler((ctx) => ({ ...ctx, useScalaVersion: true }))
|
|
|
|
.op('%')
|
|
|
|
.join(versionMatch);
|
|
|
|
|
|
|
|
function depHandler(ctx: Ctx): Ctx {
|
|
|
|
const {
|
|
|
|
scalaVersion,
|
|
|
|
groupId,
|
|
|
|
artifactId,
|
|
|
|
currentValue,
|
|
|
|
useScalaVersion,
|
|
|
|
depType,
|
|
|
|
groupName,
|
|
|
|
} = ctx;
|
|
|
|
|
|
|
|
delete ctx.groupId;
|
|
|
|
delete ctx.artifactId;
|
|
|
|
delete ctx.currentValue;
|
|
|
|
delete ctx.useScalaVersion;
|
|
|
|
delete ctx.depType;
|
|
|
|
delete ctx.groupName;
|
|
|
|
|
|
|
|
const depName = `${groupId!}:${artifactId!}`;
|
|
|
|
|
|
|
|
const dep: PackageDependency = {
|
|
|
|
datasource: SbtPackageDatasource.id,
|
2019-05-01 06:40:35 +00:00
|
|
|
depName,
|
2022-08-10 05:56:27 +00:00
|
|
|
packageName:
|
|
|
|
scalaVersion && useScalaVersion ? `${depName}_${scalaVersion}` : depName,
|
2019-05-01 06:40:35 +00:00
|
|
|
currentValue,
|
|
|
|
};
|
|
|
|
|
2022-08-10 05:56:27 +00:00
|
|
|
if (depType) {
|
|
|
|
dep.depType = depType;
|
2020-11-16 13:46:23 +00:00
|
|
|
}
|
|
|
|
|
2022-08-10 05:56:27 +00:00
|
|
|
if (depType === 'plugin') {
|
|
|
|
dep.datasource = SbtPluginDatasource.id;
|
2019-05-30 23:39:07 +00:00
|
|
|
}
|
2019-05-01 06:40:35 +00:00
|
|
|
|
2022-08-10 05:56:27 +00:00
|
|
|
if (groupName) {
|
|
|
|
dep.groupName = groupName;
|
|
|
|
}
|
2020-07-28 08:37:55 +00:00
|
|
|
|
2022-08-10 05:56:27 +00:00
|
|
|
ctx.deps.push(dep);
|
2019-05-01 06:40:35 +00:00
|
|
|
|
2022-08-10 05:56:27 +00:00
|
|
|
return ctx;
|
|
|
|
}
|
2019-05-01 06:40:35 +00:00
|
|
|
|
2022-08-10 05:56:27 +00:00
|
|
|
function depTypeHandler(ctx: Ctx, { value: depType }: { value: string }): Ctx {
|
|
|
|
return { ...ctx, depType };
|
|
|
|
}
|
2019-05-01 06:40:35 +00:00
|
|
|
|
2022-08-10 05:56:27 +00:00
|
|
|
const sbtPackageMatch = q
|
|
|
|
.opt<Ctx>(q.opt(q.sym<Ctx>('lazy')).sym('val').sym().op('='))
|
|
|
|
.alt(simpleDependencyMatch, versionedDependencyMatch)
|
|
|
|
.opt(
|
|
|
|
q.alt<Ctx>(
|
|
|
|
q.sym<Ctx>('classifier').str(depTypeHandler),
|
|
|
|
q.op<Ctx>('%').sym(depTypeHandler),
|
|
|
|
q.op<Ctx>('%').str(depTypeHandler)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
.handler(depHandler);
|
|
|
|
|
|
|
|
const sbtPluginMatch = q
|
|
|
|
.sym<Ctx>(regEx(/^(?:addSbtPlugin|addCompilerPlugin)$/))
|
|
|
|
.tree({
|
|
|
|
type: 'wrapped-tree',
|
|
|
|
maxDepth: 1,
|
|
|
|
search: q
|
|
|
|
.begin<Ctx>()
|
|
|
|
.alt(simpleDependencyMatch, versionedDependencyMatch)
|
|
|
|
.end(),
|
|
|
|
})
|
|
|
|
.handler((ctx) => ({ ...ctx, depType: 'plugin' }))
|
|
|
|
.handler(depHandler);
|
|
|
|
|
|
|
|
const resolverMatch = q
|
|
|
|
.str<Ctx>()
|
|
|
|
.sym('at')
|
|
|
|
.str((ctx, { value }) => {
|
|
|
|
if (parseUrl(value)) {
|
|
|
|
ctx.registryUrls.push(value);
|
|
|
|
}
|
|
|
|
return ctx;
|
|
|
|
});
|
|
|
|
|
|
|
|
const addResolverMatch = q.sym<Ctx>('resolvers').alt(
|
|
|
|
q.op<Ctx>('+=').join(resolverMatch),
|
|
|
|
q.op<Ctx>('++=').sym('Seq').tree({
|
|
|
|
type: 'wrapped-tree',
|
|
|
|
maxDepth: 1,
|
|
|
|
search: resolverMatch,
|
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
function registryUrlHandler(ctx: Ctx): Ctx {
|
|
|
|
for (const dep of ctx.deps) {
|
|
|
|
dep.registryUrls = [...ctx.registryUrls];
|
|
|
|
if (dep.depType === 'plugin') {
|
|
|
|
dep.registryUrls.push(SBT_PLUGINS_REPO);
|
2019-05-01 06:40:35 +00:00
|
|
|
}
|
|
|
|
}
|
2022-08-10 05:56:27 +00:00
|
|
|
return ctx;
|
|
|
|
}
|
2019-05-01 06:40:35 +00:00
|
|
|
|
2022-08-10 05:56:27 +00:00
|
|
|
const query = q.tree<Ctx>({
|
|
|
|
type: 'root-tree',
|
|
|
|
maxDepth: 32,
|
|
|
|
search: q.alt<Ctx>(
|
|
|
|
scalaVersionMatch,
|
|
|
|
packageFileVersionMatch,
|
|
|
|
sbtPackageMatch,
|
|
|
|
sbtPluginMatch,
|
|
|
|
addResolverMatch,
|
|
|
|
variableDefinitionMatch
|
|
|
|
),
|
|
|
|
postHandler: registryUrlHandler,
|
|
|
|
});
|
|
|
|
|
|
|
|
export function extractPackageFile(
|
|
|
|
content: string,
|
|
|
|
_packageFile: string
|
|
|
|
): PackageFile | null {
|
|
|
|
let parsedResult: Ctx | undefined;
|
|
|
|
|
|
|
|
try {
|
|
|
|
parsedResult = scala.query(content, query, {
|
|
|
|
vars: {},
|
|
|
|
deps: [],
|
|
|
|
registryUrls: [MAVEN_REPO],
|
2019-05-01 06:40:35 +00:00
|
|
|
});
|
2022-08-10 05:56:27 +00:00
|
|
|
} catch (err) /* istanbul ignore next */ {
|
|
|
|
logger.warn({ err }, 'Sbt parsing error');
|
2020-02-28 07:49:51 +00:00
|
|
|
}
|
2019-05-01 06:40:35 +00:00
|
|
|
|
2022-08-10 05:56:27 +00:00
|
|
|
if (!parsedResult) {
|
|
|
|
return null;
|
2020-03-17 11:15:22 +00:00
|
|
|
}
|
2022-04-19 17:46:07 +00:00
|
|
|
|
2022-08-10 05:56:27 +00:00
|
|
|
const { deps, packageFileVersion } = parsedResult;
|
2019-05-01 06:40:35 +00:00
|
|
|
|
2022-08-10 05:56:27 +00:00
|
|
|
if (!deps.length) {
|
2020-03-17 11:15:22 +00:00
|
|
|
return null;
|
|
|
|
}
|
2022-04-19 17:46:07 +00:00
|
|
|
|
2022-08-10 05:56:27 +00:00
|
|
|
return { deps, packageFileVersion };
|
2019-05-01 06:40:35 +00:00
|
|
|
}
|