Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 69 additions & 13 deletions src/language.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,56 @@

*/

import { basename } from 'path';
import { LanguageId } from './deployment';

export type Runner = 'nodejs' | 'python' | 'ruby' | 'csharp';

export interface RunnerInfo {
id: Runner;
languageId: LanguageId;
filePatterns: RegExp[];
installCommand: string;
displayName: string;
}

export const Runners: Record<Runner, RunnerInfo> = {
nodejs: {
id: 'nodejs',
languageId: 'node',
filePatterns: [/^package\.json$/],
installCommand: 'npm install',
displayName: 'NPM'
},
python: {
id: 'python',
languageId: 'py',
filePatterns: [/^requirements\.txt$/],
installCommand: 'pip install -r requirements.txt',
displayName: 'Pip'
},
ruby: {
id: 'ruby',
languageId: 'rb',
filePatterns: [/^Gemfile$/],
installCommand: 'bundle install',
displayName: 'Gem'
},
csharp: {
id: 'csharp',
languageId: 'cs',
filePatterns: [/^project\.json$/, /\.csproj$/],
installCommand: 'dotnet restore',
displayName: 'NuGet'
}
};

interface Language {
tag: string; // Tag which corresponds to language_id in metacall.json
displayName: string; // Name for displaying the language
hexColor: string; // Color for displaying the language related things
fileExtRegex: RegExp; // Regex for selecting the metacall.json scripts field
runnerName?: string; // Id of the runner
runnerName?: Runner; // Id of the runner
runnerFilesRegexes: RegExp[]; // Regex for generating the runners list
}

Expand All @@ -23,39 +65,39 @@ export const Languages: Record<LanguageId, Language> = {
hexColor: '#953dac',
fileExtRegex: /^cs$/,
runnerName: 'csharp',
runnerFilesRegexes: [/^project\.json$/, /\.csproj$/]
runnerFilesRegexes: Runners.csharp.filePatterns
},
py: {
tag: 'py',
displayName: 'Python',
hexColor: '#ffd43b',
fileExtRegex: /^py$/,
runnerName: 'python',
runnerFilesRegexes: [/^requirements\.txt$/]
runnerFilesRegexes: Runners.python.filePatterns
},
rb: {
tag: 'rb',
displayName: 'Ruby',
hexColor: '#e53935',
fileExtRegex: /^rb$/,
runnerName: 'ruby',
runnerFilesRegexes: [/^Gemfile$/]
runnerFilesRegexes: Runners.ruby.filePatterns
},
node: {
tag: 'node',
displayName: 'NodeJS',
hexColor: '#3c873a',
fileExtRegex: /^js$/,
runnerName: 'nodejs',
runnerFilesRegexes: [/^package\.json$/]
runnerFilesRegexes: Runners.nodejs.filePatterns
},
ts: {
tag: 'ts',
displayName: 'TypeScript',
hexColor: '#007acc',
fileExtRegex: /^(ts|tsx)$/,
runnerName: 'nodejs',
runnerFilesRegexes: [/^package\.json$/] // TODO: Use tsconfig instead?
runnerFilesRegexes: Runners.nodejs.filePatterns
},
file: {
tag: 'file',
Expand Down Expand Up @@ -94,12 +136,26 @@ export const DisplayNameToLanguageId: Record<string, LanguageId> = Object.keys(
);

export const RunnerToDisplayName = (runner: string): string => {
const displayNameMap: Record<string, string> = {
nodejs: 'NPM',
python: 'Pip',
ruby: 'Gem',
csharp: 'NuGet'
};
const match = Runners[runner as Runner];

return match ? match.displayName : 'Build';
};

export const detectRunnersFromFiles = (files: string[]): Runner[] => {
const runners = new Set<Runner>();

for (const file of files) {
const fileName = basename(file);

for (const runner of Object.values(Runners)) {
for (const pattern of runner.filePatterns) {
if (pattern.exec(fileName)) {
runners.add(runner.id);
break;
}
}
}
}

return displayNameMap[runner] || 'Build';
return Array.from(runners);
};
27 changes: 9 additions & 18 deletions src/package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import walk from 'ignore-walk';
import { basename, extname } from 'path';
import { LanguageId, MetaCallJSON } from './deployment';
import { Languages } from './language';
import { detectRunnersFromFiles, Languages, Runner } from './language';

export const findFilesPath = async (
path: string = process.cwd(),
Expand All @@ -36,23 +36,14 @@ export const pathIsMetaCallJson = (path: string): boolean =>
export const findMetaCallJsons = (files: string[]): string[] =>
files.filter(pathIsMetaCallJson);

export const findRunners = (files: string[]): Set<string> => {
const runners: Set<string> = new Set<string>();

for (const file of files) {
const fileName = basename(file);
for (const langId of Object.keys(Languages)) {
const lang = Languages[langId as LanguageId];
for (const re of lang.runnerFilesRegexes) {
if (re.exec(fileName) && lang.runnerName) {
runners.add(lang.runnerName);
}
}
}
}
export const findRunners = (files: string[]): Set<Runner> =>
new Set<Runner>(detectRunnersFromFiles(files));

return runners;
};
export const detectRunners = async (
path: string = process.cwd(),
ignoreFiles: string[] = ['.gitignore']
): Promise<Runner[]> =>
detectRunnersFromFiles(await findFilesPath(path, ignoreFiles));

export enum PackageError {
Empty = 'No files found in the current folder',
Expand All @@ -64,7 +55,7 @@ interface PackageDescriptor {
error: PackageError;
files: string[];
jsons: string[];
runners: string[];
runners: Runner[];
}

const NullPackage: PackageDescriptor = {
Expand Down
23 changes: 23 additions & 0 deletions src/test/package.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { deepStrictEqual } from 'assert';
import { join } from 'path';
import { MetaCallJSON } from '../deployment';
import { detectRunnersFromFiles, Runners } from '../language';
import {
findFilesPath,
findMetaCallJsons,
Expand Down Expand Up @@ -127,6 +128,28 @@ describe('Unit Package', function () {
deepStrictEqual(Array.from(findRunners(files)), expectedRunners);
});

it('detectRunnersFromFiles all', async () => {
const runnersPath = join(basePath, 'runners');
const expectedRunners: string[] = [
'csharp',
'ruby',
'nodejs',
'python'
];
const files = await findFilesPath(runnersPath);
deepStrictEqual(detectRunnersFromFiles(files), expectedRunners);
});

it('runners include install commands', () => {
deepStrictEqual(Runners.nodejs.installCommand, 'npm install');
deepStrictEqual(
Runners.python.installCommand,
'pip install -r requirements.txt'
);
deepStrictEqual(Runners.ruby.installCommand, 'bundle install');
deepStrictEqual(Runners.csharp.installCommand, 'dotnet restore');
});

it('generateJsonsFromFiles', async () => {
const runnersPath = join(basePath, 'runners');
const files = await findFilesPath(runnersPath);
Expand Down