Skip to content

Commit

Permalink
Merge pull request #451 from TCourtneyOwen/move-single-host-creation-…
Browse files Browse the repository at this point in the history
…to-project-repos

Call 'convert-to-single-host' in generated project
  • Loading branch information
TCourtneyOwen authored May 28, 2019
2 parents c90ea66 + 2da0ce8 commit a2aabe7
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 121 deletions.
113 changes: 0 additions & 113 deletions src/app/helpers/helperMethods.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,4 @@
import * as _ from 'lodash';
import * as util from "util";
const fs = require('fs');
const readFileAsync = util.promisify(fs.readFile);
const unlinkFileAsync = util.promisify(fs.unlink);
const writeFileAsync = util.promisify(fs.writeFile);
const path = require('path');
const hosts = [
"excel",
"onenote",
"outlook",
"powerpoint",
"project",
"word"
];

export namespace helperMethods {
export function deleteFolderRecursively(projectFolder: string)
Expand Down Expand Up @@ -46,103 +32,4 @@ export namespace helperMethods {
}
return false;
};

export function modifyProjectForSingleHost(projectFolder: string, projectType: string, host: string, typescript: boolean) {
return new Promise(async (resolve, reject) => {
try {
await convertProjectToSingleHost(projectFolder, projectType, host, typescript);
await updatePackageJsonForSingleHost(projectFolder, host);
return resolve();
} catch (err){
return reject(err);
}
});
}

async function convertProjectToSingleHost(projectFolder: string, projectType: string, host: string, typescript: boolean): Promise<void> {
try {
let extension = typescript ? "ts" : "js";
// copy host-specific manifest over manifest.xml
const manifestContent: any = await readFileAsync(path.resolve(`${projectFolder}/manifest.${host}.xml`), 'utf8');
await writeFileAsync(path.resolve(`${projectFolder}/manifest.xml`), manifestContent);

switch (projectType) {
case "taskpane":
{
// copy host-specific taskpane.ts[js] over src/taskpane/taskpane.ts[js]
const srcContent = await readFileAsync(path.resolve(`${projectFolder}/src/taskpane/${host}.${extension}`), 'utf8');
await writeFileAsync(path.resolve(`${projectFolder}/src/taskpane/taskpane.${extension}`), srcContent);

// delete all host specific files
hosts.forEach(async function (host) {
await unlinkFileAsync(path.resolve(`${projectFolder}/manifest.${host}.xml`));
await unlinkFileAsync(path.resolve(`${projectFolder}/src/taskpane/${host}.${extension}`));
});
break;
}
case "angular":
{
// copy host-specific app.component.ts[js] over src/taskpane/app/app.component.ts[js]
const srcContent = await readFileAsync(path.resolve(`${projectFolder}/src/taskpane/app/${host}.app.component.${extension}`), 'utf8');
await writeFileAsync(path.resolve(`${projectFolder}/src/taskpane/app/app.component.${extension}`), srcContent);

// delete all host specific files
hosts.forEach(async function (host) {
await unlinkFileAsync(path.resolve(`${projectFolder}/manifest.${host}.xml`));
await unlinkFileAsync(path.resolve(`${projectFolder}/src/taskpane/app/${host}.app.component.${extension}`));
});
break;
}
case "react":
{
// copy host-specific App.tsx[js] over src/taskpane/app/components/App.tsx[js]
extension = typescript ? "tsx" : "js";
const srcContent = await readFileAsync(path.resolve(`${projectFolder}/src/taskpane/components/${_.upperFirst(host)}.App.${extension}`), 'utf8');
await writeFileAsync(path.resolve(`${projectFolder}/src/taskpane/components/App.${extension}`), srcContent);

// delete all host specific files
hosts.forEach(async function (host) {
await unlinkFileAsync(path.resolve(`${projectFolder}/manifest.${host}.xml`));
await unlinkFileAsync(path.resolve(`${projectFolder}/src/taskpane/components/${_.upperFirst(host)}.App.${extension}`));
});
break;
}
default:
throw new Error("Invalid project type");
}
} catch(err) {
throw new Error(err);
}
}

async function updatePackageJsonForSingleHost(projectFolder:string, host: string): Promise<void> {
try {
// update package.json to reflect selected host
const packageJson = path.resolve(`${projectFolder}/package.json`);
const data: any = await readFileAsync(packageJson, 'utf8');
let content = JSON.parse(data);

// update 'config' section in package.json to use selected host
content.config["app-to-debug"] = host;

// remove scripts from package.json that are unrelated to selected host,
// and update sideload and unload scripts to use selected host.
Object.keys(content.scripts).forEach(function (key) {
if (key.includes("sideload:") || key.includes("unload:")) {
delete content.scripts[key];
}
switch (key) {
case "sideload":
case "unload":
content.scripts[key] = content.scripts[`${key}:${host}`];
break;
}
});

// write updated json to file
await writeFileAsync(packageJson, JSON.stringify(content, null, 4));
} catch (err) {
throw new Error(err);
}
}
}
9 changes: 7 additions & 2 deletions src/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
import * as _ from 'lodash';
import * as appInsights from 'applicationinsights';
import * as chalk from 'chalk';
import * as childProcess from "child_process";
import * as fs from 'fs';
import * as path from "path";
import { promisify } from "util";
import * as uuid from 'uuid/v4';
import * as yosay from 'yosay';
import * as yo from 'yeoman-generator';
Expand All @@ -16,6 +18,7 @@ import { modifyManifestFile } from 'office-addin-manifest';

let insight = appInsights.getClient('1ced6a2f-b3b2-4da5-a1b8-746512fbc840');
let git = require("simple-git");
const childProcessExec = promisify(childProcess.exec);
const excelCustomFunctions = `excel-functions`;
const manifest = 'manifest';
const typescript = `TypeScript`;
Expand Down Expand Up @@ -259,7 +262,7 @@ module.exports = yo.extend({
insight.trackException(new Error('Configuration Error: ' + err));
}
},

_copyProjectFiles()
{
return new Promise((resolve, reject) => {
Expand All @@ -274,7 +277,9 @@ module.exports = yo.extend({
git().clone(projectRepoBranchInfo.repo, this.destinationPath(), ['--branch', projectRepoBranchInfo.branch || 'master'], async (err) => {
// for all project types other than Excel Custom Functions. modify the generated project so it targets the selected host
if (!this.project.isExcelFunctionsProject) {
await helperMethods.modifyProjectForSingleHost(this.destinationPath(), _.toLower(this.project.projectType), _.toLower(this.project.hostInternalName), language == 'ts');
// Call 'convert-to-single-host' npm script in generated project, passing in host parameter
const cmdLine = `npm run convert-to-single-host --if-present -- ${_.toLower(this.project.hostInternalName)}`;
await childProcessExec(cmdLine);
}

// modify manifest guid and DisplayName
Expand Down
205 changes: 205 additions & 0 deletions src/test/convert-to-single-host.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
* See LICENSE in the project root for license information.
*/
import * as assert from 'yeoman-assert';
import * as fs from "fs";
import * as helpers from 'yeoman-test';
import { readManifestFile } from "office-addin-manifest";
import * as path from 'path';
import { promisify } from "util";
const readFileAsync = promisify(fs.readFile);
const hosts = ["excel", "onenote", "outlook", "powerpoint", "project", "word"];
const manifestFile = "manifest.xml";
const packageJsonFile = "package.json";
const unexpectedManifestFiles = [
'manifest.excel.xml',
'manifest.onenote.xml',
'manifest.outlook.xml',
'manifest.powerpoint.xml',
'manifest.project.xml',
'manifest.word.xml',
]

// Test to verify converting a project to a single host
// for Office-Addin-Taskpane Typescript project using Excel host
describe('Office-Add-Taskpane-Ts projects', () => {
const expectedFiles = [
packageJsonFile,
manifestFile,
'src/taskpane/taskpane.ts',
]
const unexpectedFiles = [
'src/taskpane/excel.ts',
'src/taskpane/onenote.ts',
'src/taskpane/outlook.ts',
'src/taskpane/powerpoint.ts',
'src/taskpane/project.ts',
'src/taskpane/word.ts'
]
let answers = {
projectType: "taskpane",
scriptType: "TypeScript",
name: "TaskpaneProject",
host: hosts[0]
};

describe('Office-Add-Taskpane project', () => {
before((done) => {
helpers.run(path.join(__dirname, '../app')).withPrompts(answers).on('end', done);
});

it('creates expected files', (done) => {
assert.file(expectedFiles);
assert.noFile(unexpectedFiles);
assert.noFile(unexpectedManifestFiles);
done();
});
});

describe('Package.json is updated appropriately', () => {
it('Package.json is updated properly', async () => {
const data: any = await readFileAsync(packageJsonFile, 'utf8');
let content = JSON.parse(data);
assert.equal(content.config["app-to-debug"], hosts[0]);

// Verify host-specific sideload and unload sripts have been removed
let unexexpectedScriptsFound: boolean = false;
Object.keys(content.scripts).forEach(function (key) {
if (key.includes("sideload:") || key.includes("unload:")) {
unexexpectedScriptsFound = true;
}
});
assert.equal(unexexpectedScriptsFound, false);
});
});

describe('Manifest.xml is updated appropriately', () => {
it('Manifest.xml is updated appropriately', async () => {
const manifestInfo = await readManifestFile(manifestFile);
assert.equal(manifestInfo.hosts, "Workbook");
});
});
});

// Test to verify converting a project to a single host
// for Angular JavaScript project using Word host
describe('Office-Add-Taskpane-Angular-Js project', () => {
const expectedFiles = [
packageJsonFile,
manifestFile,
'src/taskpane/app/app.component.js',
]
const unexpectedFiles = [
'src/taskpane/app/excel.app.component.js',
'src/taskpane/app/onenote.app.component.ts',
'src/taskpane/app/outlook.app.component.js',
'src/taskpane/app/powerpoint.app.component.js',
'src/taskpane/app/project.app.component.js',
'src/taskpane/app/word.app.component.ts',
]
let answers = {
projectType: "angular",
scriptType: "JavaScript",
name: "AngularProject",
host: hosts[5]
};

describe('Office-Add-Taskpane project', () => {
before((done) => {
helpers.run(path.join(__dirname, '../app')).withPrompts(answers).on('end', done);
});

it('creates expected files', (done) => {
assert.file(expectedFiles);
assert.noFile(unexpectedFiles);
assert.noFile(unexpectedManifestFiles);
done();
});
});

describe('Package.json is updated appropriately', () => {
it('Package.json is updated properly', async () => {
const data: any = await readFileAsync(packageJsonFile, 'utf8');
let content = JSON.parse(data);
assert.equal(content.config["app-to-debug"], hosts[5]);

// Verify host-specific sideload and unload sripts have been removed
let unexexpectedScriptsFound: boolean = false;
Object.keys(content.scripts).forEach(function (key) {
if (key.includes("sideload:") || key.includes("unload:")) {
unexexpectedScriptsFound = true;
}
});
assert.equal(unexexpectedScriptsFound, false);
});
});

describe('Manifest.xml is updated appropriately', () => {
it('Manifest.xml is updated appropriately', async () => {
const manifestInfo = await readManifestFile(manifestFile);
assert.equal(manifestInfo.hosts, "Document");
});
});
});

// Test to verify converting a project to a single host
// for React Typescript project using PowerPoint host
describe('Office-Add-Taskpane-React-Ts project', () => {
const expectedFiles = [
packageJsonFile,
manifestFile,
'src/taskpane/components/App.tsx', ,
]
const unexpectedFiles = [
'src/taskpane/components/Excel.App.tsx',
'src/taskpane/components/Onenote.App.tsx',
'src/taskpane/components/Outlook.App.tsx',
'src/taskpane/components/PowerPoint.App.tsx',
'src/taskpane/components/Project.App.tsx',
'src/taskpane/components/Word.App.tsx',
]
let answers = {
projectType: "react",
scriptType: "TypeScript",
name: "ReactProject",
host: hosts[3]
};

describe('Office-Add-Taskpane project', () => {
before((done) => {
helpers.run(path.join(__dirname, '../app')).withPrompts(answers).on('end', done);
});

it('creates expected files', (done) => {
assert.file(expectedFiles);
assert.noFile(unexpectedFiles);
assert.noFile(unexpectedManifestFiles);
done();
});
});

describe('Package.json is updated appropriately', () => {
it('Package.json is updated properly', async () => {
const data: any = await readFileAsync(packageJsonFile, 'utf8');
let content = JSON.parse(data);
assert.equal(content.config["app-to-debug"], hosts[3]);

// Verify host-specific sideload and unload sripts have been removed
let unexexpectedScriptsFound: boolean = false;
Object.keys(content.scripts).forEach(function (key) {
if (key.includes("sideload:") || key.includes("unload:")) {
unexexpectedScriptsFound = true;
}
});
assert.equal(unexexpectedScriptsFound, false);
});
});

describe('Manifest.xml is updated appropriately', () => {
it('Manifest.xml is updated appropriately', async () => {
const manifestInfo = await readManifestFile(manifestFile);
assert.equal(manifestInfo.hosts, "Presentation");
});
});
});
6 changes: 0 additions & 6 deletions src/test/manifest-only-project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,6 @@ describe('manifest project - answers & args', () => {
helpers.run(path.join(__dirname, '../app')).withArguments(argument).withPrompts(answers).on('end', done);
});

console.log("validating files")

it('creates expected files', (done) => {
assert.file(expectedFiles);
assert.noFile(unexpectedFiles);
Expand All @@ -98,8 +96,6 @@ describe('manifest project - answers & args', () => {
helpers.run(path.join(__dirname, '../app')).withArguments(argument).withPrompts(answers).on('end', done);
});

console.log("validating files")

it('creates expected files', (done) => {
assert.file(expectedFiles);
assert.noFile(unexpectedFiles);
Expand All @@ -124,8 +120,6 @@ describe('manifest project - answers & args', () => {
helpers.run(path.join(__dirname, '../app')).withArguments(argument).withPrompts(answers).on('end', done);
});

console.log("validating files")

it('creates expected files', (done) => {
assert.file(expectedFiles);
assert.noFile(unexpectedFiles);
Expand Down

0 comments on commit a2aabe7

Please sign in to comment.