Skip to content
Merged
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
109 changes: 109 additions & 0 deletions scripts/localInstall.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#!/usr/bin/env node

const shell = require('shelljs');
const fs = require('fs');
const path = require('path');
const { run, execSilent } = require('./util');

/**
* Script for setting up the library in another local project
*
* ### Usage
*
* 1) `./localInstall install /path/to/target`
*
* Generate and install a tarball package to a target npm module. Useful for locally
* testing a build that will be sent to NPM. Accepts a relative or absolute path.
*
* 2) `./localInstall.js link /path/to/target`.
*
* Link the library to another project for development purposes. Changes made in this
* project will be automatically reflected in the target module.
*
* 3) `./localInstall.js unlink /path/to/target`
*
* Unlink the library from another project.
*/

const COMMANDS = ['install', 'link', 'unlink'];

function showHelp() {
console.log('Commands:');
console.log(
' install [path to target module]\tCreates an NPM package of the project and installs it to the target module'
);
console.log(' link [path to target module]\t\tLink the project to another module for quick development');
console.log(' unlink [path to target module]\tUnlink the project from the target module');
}

function main() {
const command = process.argv[2];
if (!COMMANDS.includes(command)) {
showHelp();
process.exit(0);
}

const targetPackagePath = !path.isAbsolute(process.argv[3])
? path.resolve(process.cwd(), process.argv[3])
: process.argv[3];

if (!fs.existsSync(targetPackagePath)) {
console.log(`A valid target package path is required`);
process.exit(1);
}

const isDirectory = fs.lstatSync(targetPackagePath).isDirectory();
const packageJsonPath = path.join(targetPackagePath, 'package.json');
if (!isDirectory || !fs.existsSync(packageJsonPath)) {
console.log('Path must be to a valid npm package');
process.exit(1);
}

const localPackagePath = path.join(__dirname, '..');
const { name, version } = JSON.parse(fs.readFileSync(path.join(localPackagePath, 'package.json')).toString());

switch (command) {
case COMMANDS[0]: // install
let tarballPath;
run('Building project and creating package', () => {
shell.cd(localPackagePath);
execSilent('yarn build');
execSilent('yarn pack');
tarballPath = execSilent('find $(pwd) -type f -iname *.tgz').replace('\n', '');
});

run(`Installing v${version} to ${targetPackagePath}`, () => {
shell.cd(targetPackagePath);
const yarnLockPath = path.join(targetPackagePath, 'yarn.lock');
if (fs.existsSync(yarnLockPath)) {
execSilent(`yarn remove ${name}`, true);
execSilent(`yarn cache clean`);
execSilent(`yarn add ${tarballPath}`);
} else {
execSilent(`npm uninstall ${name}`);
execSilent(`npm install ${tarballPath}`);
}
});
break;
case COMMANDS[1]: // link
run(`Linking ${name} to ${targetPackagePath}`, () => {
shell.cd(localPackagePath);
execSilent('yarn link');
shell.cd(targetPackagePath);
execSilent(`yarn link ${name}`);
});
break;
case COMMANDS[2]: // unlink
run(`Unlinking ${name} from ${targetPackagePath}`, () => {
shell.cd(targetPackagePath);
execSilent(`yarn unlink ${name}`);
shell.cd(localPackagePath);
execSilent('yarn unlink');
});
break;
default:
showHelp();
}
}

main();
41 changes: 41 additions & 0 deletions scripts/util.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const shell = require('shelljs');
require('shelljs/global');

const terminalCodes = {
Red: '\\033[31m',
LightGrey: '\\033[37m',
Bold: '\\033[1m',
ResetAll: '\\033[0m',
};

module.exports = {
run: (status, f) => {
let result;
shell.exec(`printf "🐎 ${status}..."`);
const { LightGrey, Bold, ResetAll } = terminalCodes;
try {
result = f();
} catch (e) {
shell.exec(`printf "\\r❗️ ${status}...${Bold}${LightGrey}failed${ResetAll}\\n"`);
shell.exec(`printf "${e.message}"`);
process.exit(1);
}
shell.exec(`printf "\\r✅ ${status}...${Bold}${LightGrey}done${ResetAll}\\n"`);
return result;
},
execSilent: (command, swallowError) => {
const prevConfig = config.fatal;
config.fatal = true;
try {
return shell.exec(command, { silent: true });
} catch (e) {
if (swallowError) {
return;
}
throw e;
} finally {
config.fatal = prevConfig;
}
},
terminalCodes,
};
4 changes: 2 additions & 2 deletions src/resolve/metadataResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,9 +320,9 @@ const parseAsFolderMetadataXml =
if (parts.length > 1) {
const folderContentTypesDirs = getFolderContentTypeDirNames(registry);
// check if the path contains a folder content name as a directory
// e.g., `/reports/` and if it does return that folder name.
const pathWithoutFile = parts.slice(0, -1);
folderContentTypesDirs.some((dirName) => {
if (fsPath.includes(`${sep}${dirName}${sep}`)) {
if (pathWithoutFile.includes(dirName)) {
folderName = dirName;
}
});
Expand Down
25 changes: 25 additions & 0 deletions test/resolve/metadataResolver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,31 @@ describe('MetadataResolver', () => {
expect(comp[0]).to.have.deep.property('type', registryAccess.getTypeByName('ReportFolder'));
});

it('should resolve folder metadata and reports from zip paths without leading directory', () => {
const registryAccess = new RegistryAccess();
const reportFolderDir = 'reports';
const reportSubFolder = join(reportFolderDir, 'TestFolder');
const virtualFS: VirtualDirectory[] = [
{ dirPath: reportFolderDir, children: ['TestFolder-meta.xml', 'TestFolder'] },
{ dirPath: reportSubFolder, children: ['MyReport.report-meta.xml'] },
];
const tree = new VirtualTreeContainer(virtualFS);
const mdResolver = new MetadataResolver(registryAccess, tree);

const components = mdResolver.getComponentsFromPath(reportFolderDir);
expect(components).to.be.an('array').with.lengthOf(2);

const folderComp = components.find((c) => c.type.name === 'ReportFolder');
expect(folderComp).to.exist;
expect(folderComp).to.have.property('name', 'TestFolder');
expect(folderComp).to.have.deep.property('type', registryAccess.getTypeByName('ReportFolder'));

const reportComp = components.find((c) => c.type.name === 'Report');
expect(reportComp).to.exist;
expect(reportComp).to.have.property('name', 'TestFolder/MyReport');
expect(reportComp).to.have.deep.property('type', registryAccess.getTypeByName('Report'));
});

it('Should not mistake folder component of a mixed content type as that type', () => {
// this test has coveage on non-mixedContent types as well by nature of the execution path
const path = mixedContentInFolder.FOLDER_XML_PATH;
Expand Down
Loading