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
12 changes: 9 additions & 3 deletions app/exec/extension/_lib/merger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { glob } from "glob";
import jju = require("jju");
import jsonInPlace = require("json-in-place");
import loc = require("./loc");
import Module = require("module");
import path = require("path");
import trace = require("../../../lib/trace");
import version = require("../../../lib/dynamicVersion");
Expand Down Expand Up @@ -96,7 +97,12 @@ export class Merger {
});

const fullJsFile = path.resolve(this.settings.manifestJs);
const manifestModuleFn = require(fullJsFile);
const source = fs.readFileSync(fullJsFile, "utf8");
const loadedModule: any = new (Module as any)(fullJsFile, module);
loadedModule.filename = fullJsFile;
loadedModule.paths = (Module as any)._nodeModulePaths(path.dirname(fullJsFile));
loadedModule._compile(source, fullJsFile);
const manifestModuleFn = loadedModule.exports;
if (!manifestModuleFn || typeof manifestModuleFn != "function") {
throw new Error(`Missing export function from manifest-js file ${fullJsFile}`)
}
Expand Down Expand Up @@ -447,11 +453,11 @@ export class Merger {
// Validate task.json files for this contribution with backwards compatibility checking
if (contributionTaskJsonPaths.length > 0) {
trace.debug(`Validating ${contributionTaskJsonPaths.length} task.json files for contribution ${contrib.id || taskPath}`);

for (const taskJsonPath of contributionTaskJsonPaths) {
validate(taskJsonPath, "no task.json in specified directory", contributionTaskJsonPaths);
}

// Also collect for global tracking if needed
allTaskJsonPaths.push(...contributionTaskJsonPaths);
} else {
Expand Down
9 changes: 7 additions & 2 deletions app/lib/jsonvalidate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ export interface TaskJson {
id: string;
}

function readJsonFile(jsonFilePath: string): any {
const jsonData = fs.readFileSync(jsonFilePath, "utf8").replace(/^\uFEFF/, "");
return JSON.parse(jsonData);
}

/*
* Checks a json file for correct formatting against some validation function
* @param jsonFilePath path to the json file being validated
Expand All @@ -24,7 +29,7 @@ export function validate(jsonFilePath: string, jsonMissingErrorMessage?: string,

var taskJson;
try {
taskJson = require(jsonFilePath);
taskJson = readJsonFile(jsonFilePath);
} catch (jsonError) {
trace.debug("Invalid task json: %s", jsonError);
throw new Error("Invalid task json: " + jsonError);
Expand Down Expand Up @@ -88,7 +93,7 @@ export function validateRunner(taskData: any, allMatchedPaths?: string[]) {
for (const matchedPath of allMatchedPaths) {
let matchedTaskData;
try {
matchedTaskData = require(matchedPath);
matchedTaskData = readJsonFile(matchedPath);
} catch {
continue;
}
Expand Down
131 changes: 92 additions & 39 deletions app/lib/loader.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,87 @@
import colors = require("colors");
import common = require("./common");
import fsUtils = require("./fsUtils");
import path = require("path");
import trace = require("./trace");
import { TfCommand } from "./tfcommand";
import { promisify } from "util";
import { lstat } from "fs";
import * as defaultCommand from "../exec/default";
import * as loginCommand from "../exec/login";
import * as logoutCommand from "../exec/logout";
import * as resetCommand from "../exec/reset";
import * as versionCommand from "../exec/version";
import * as buildDefaultCommand from "../exec/build/default";
import * as buildListCommand from "../exec/build/list";
import * as buildQueueCommand from "../exec/build/queue";
import * as buildShowCommand from "../exec/build/show";
import * as buildTasksCreateCommand from "../exec/build/tasks/create";
import * as buildTasksDefaultCommand from "../exec/build/tasks/default";
import * as buildTasksDeleteCommand from "../exec/build/tasks/delete";
import * as buildTasksListCommand from "../exec/build/tasks/list";
import * as buildTasksUploadCommand from "../exec/build/tasks/upload";
import * as extensionCreateCommand from "../exec/extension/create";
import * as extensionDefaultCommand from "../exec/extension/default";
import * as extensionInitCommand from "../exec/extension/init";
import * as extensionInstallCommand from "../exec/extension/install";
import * as extensionIsValidCommand from "../exec/extension/isvalid";
import * as extensionPublishCommand from "../exec/extension/publish";
import * as extensionResourcesCreateCommand from "../exec/extension/resources/create";
import * as extensionResourcesDefaultCommand from "../exec/extension/resources/default";
import * as extensionShareCommand from "../exec/extension/share";
import * as extensionShowCommand from "../exec/extension/show";
import * as extensionUnpublishCommand from "../exec/extension/unpublish";
import * as extensionUnshareCommand from "../exec/extension/unshare";
import * as workitemCreateCommand from "../exec/workitem/create";
import * as workitemDefaultCommand from "../exec/workitem/default";
import * as workitemQueryCommand from "../exec/workitem/query";
import * as workitemShowCommand from "../exec/workitem/show";
import * as workitemUpdateCommand from "../exec/workitem/update";

export interface CommandFactory {
getCommand: (args: string[]) => TfCommand<any, any> | Promise<TfCommand<any, any>>;
}

const commandModules: { [key: string]: CommandFactory } = {
"default": defaultCommand,
"login": loginCommand,
"logout": logoutCommand,
"reset": resetCommand,
"version": versionCommand,
"build/default": buildDefaultCommand,
"build/list": buildListCommand,
"build/queue": buildQueueCommand,
"build/show": buildShowCommand,
"build/tasks/create": buildTasksCreateCommand,
"build/tasks/default": buildTasksDefaultCommand,
"build/tasks/delete": buildTasksDeleteCommand,
"build/tasks/list": buildTasksListCommand,
"build/tasks/upload": buildTasksUploadCommand,
"extension/create": extensionCreateCommand,
"extension/default": extensionDefaultCommand,
"extension/init": extensionInitCommand,
"extension/install": extensionInstallCommand,
"extension/isvalid": extensionIsValidCommand,
"extension/publish": extensionPublishCommand,
"extension/resources/create": extensionResourcesCreateCommand,
"extension/resources/default": extensionResourcesDefaultCommand,
"extension/share": extensionShareCommand,
"extension/show": extensionShowCommand,
"extension/unpublish": extensionUnpublishCommand,
"extension/unshare": extensionUnshareCommand,
"workitem/create": workitemCreateCommand,
"workitem/default": workitemDefaultCommand,
"workitem/query": workitemQueryCommand,
"workitem/show": workitemShowCommand,
"workitem/update": workitemUpdateCommand,
};

function getCommandModule(execPath: string[]): CommandFactory {
const commandKey = execPath.join("/");
if (!commandKey) {
return commandModules["default"];
}

return commandModules[commandKey] || commandModules[`${commandKey}/default`];
}

/**
* Load the module given by execPath and instantiate a TfCommand using args.
* @param {string[]} execPath: path to the module to load. This module must implement CommandFactory.
Expand All @@ -20,41 +91,23 @@ export interface CommandFactory {
export function load(execPath: string[], args): Promise<TfCommand<any, any>> {
trace.debug("loader.load");
let commandModulePath = path.resolve(common.APP_ROOT, "exec", execPath.join("/"));
return fsUtils.exists(commandModulePath).then(exists => {
let resolveDefaultPromise = Promise.resolve(commandModulePath);
if (exists) {
// If this extensionless path exists, it should be a directory.
// If the path doesn't exist, for now we assume that a file with a .js extension
// exists (if it doens't, we will find out below).
resolveDefaultPromise = promisify(lstat)(commandModulePath).then(stats => {
if (stats.isDirectory()) {
return path.join(commandModulePath, "default");
}
return commandModulePath;
});
try {
const commandModule = getCommandModule(execPath);
if (!commandModule) {
throw new Error(
commandModulePath + " is not a recognized command. Run with --help to see available commands.",
);
}
return resolveDefaultPromise.then((commandModulePath: string) => {
let commandModule: CommandFactory;
return fsUtils.exists(path.resolve(commandModulePath + ".js")).then(exists => {
if (!exists) {
throw new Error(
commandModulePath + " is not a recognized command. Run with --help to see available commands.",
);
}
try {
commandModule = require(commandModulePath);
} catch (e) {
trace.error(commandModulePath + " could not be fully loaded as a tfx command.");
throw e;
}
if (!commandModule.getCommand) {
throw new Error(
"Command modules must export a function, getCommand, that takes no arguments and returns an instance of TfCommand",
);
}

return commandModule.getCommand(args);
});
});
});

if (!commandModule.getCommand) {
throw new Error(
"Command modules must export a function, getCommand, that takes no arguments and returns an instance of TfCommand",
);
}

return Promise.resolve(commandModule.getCommand(args));
} catch (e) {
trace.error(commandModulePath + " could not be fully loaded as a tfx command.");
return Promise.reject(e);
}
}
8 changes: 6 additions & 2 deletions app/lib/version.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import common = require("./common");
import path = require("path");
import { DynamicVersion } from "./dynamicVersion";
import { readFile } from "fs";
import { promisify } from "util";

export class SemanticVersion extends DynamicVersion {
constructor(public major: number, public minor: number, public patch: number) {
Expand All @@ -25,6 +27,8 @@ export class SemanticVersion extends DynamicVersion {
}

export function getTfxVersion(): Promise<SemanticVersion> {
let packageJson = require(path.join(common.APP_ROOT, "package.json"));
return Promise.resolve(SemanticVersion.parse(packageJson.version));
return promisify(readFile)(path.join(common.APP_ROOT, "package.json"), "utf8").then(packageJsonContents => {
const packageJson = JSON.parse(packageJsonContents);
return SemanticVersion.parse(packageJson.version);
});
}
Loading