Skip to content

Commit d52ebd0

Browse files
@W-21000456 [Version Retrieve] [CLI] Package Version Retrieve: folder-meta.xml files not extracted from developer zips (#1679)
* fix: update metadata resolver to handle paths without leading separators * chore: remove local install scripts from PR * fix: skip file name when checking for folder content dirs * chore: add in scripts needed for local linking
1 parent 76323c7 commit d52ebd0

File tree

4 files changed

+177
-2
lines changed

4 files changed

+177
-2
lines changed

scripts/localInstall.js

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
#!/usr/bin/env node
2+
3+
const shell = require('shelljs');
4+
const fs = require('fs');
5+
const path = require('path');
6+
const { run, execSilent } = require('./util');
7+
8+
/**
9+
* Script for setting up the library in another local project
10+
*
11+
* ### Usage
12+
*
13+
* 1) `./localInstall install /path/to/target`
14+
*
15+
* Generate and install a tarball package to a target npm module. Useful for locally
16+
* testing a build that will be sent to NPM. Accepts a relative or absolute path.
17+
*
18+
* 2) `./localInstall.js link /path/to/target`.
19+
*
20+
* Link the library to another project for development purposes. Changes made in this
21+
* project will be automatically reflected in the target module.
22+
*
23+
* 3) `./localInstall.js unlink /path/to/target`
24+
*
25+
* Unlink the library from another project.
26+
*/
27+
28+
const COMMANDS = ['install', 'link', 'unlink'];
29+
30+
function showHelp() {
31+
console.log('Commands:');
32+
console.log(
33+
' install [path to target module]\tCreates an NPM package of the project and installs it to the target module'
34+
);
35+
console.log(' link [path to target module]\t\tLink the project to another module for quick development');
36+
console.log(' unlink [path to target module]\tUnlink the project from the target module');
37+
}
38+
39+
function main() {
40+
const command = process.argv[2];
41+
if (!COMMANDS.includes(command)) {
42+
showHelp();
43+
process.exit(0);
44+
}
45+
46+
const targetPackagePath = !path.isAbsolute(process.argv[3])
47+
? path.resolve(process.cwd(), process.argv[3])
48+
: process.argv[3];
49+
50+
if (!fs.existsSync(targetPackagePath)) {
51+
console.log(`A valid target package path is required`);
52+
process.exit(1);
53+
}
54+
55+
const isDirectory = fs.lstatSync(targetPackagePath).isDirectory();
56+
const packageJsonPath = path.join(targetPackagePath, 'package.json');
57+
if (!isDirectory || !fs.existsSync(packageJsonPath)) {
58+
console.log('Path must be to a valid npm package');
59+
process.exit(1);
60+
}
61+
62+
const localPackagePath = path.join(__dirname, '..');
63+
const { name, version } = JSON.parse(fs.readFileSync(path.join(localPackagePath, 'package.json')).toString());
64+
65+
switch (command) {
66+
case COMMANDS[0]: // install
67+
let tarballPath;
68+
run('Building project and creating package', () => {
69+
shell.cd(localPackagePath);
70+
execSilent('yarn build');
71+
execSilent('yarn pack');
72+
tarballPath = execSilent('find $(pwd) -type f -iname *.tgz').replace('\n', '');
73+
});
74+
75+
run(`Installing v${version} to ${targetPackagePath}`, () => {
76+
shell.cd(targetPackagePath);
77+
const yarnLockPath = path.join(targetPackagePath, 'yarn.lock');
78+
if (fs.existsSync(yarnLockPath)) {
79+
execSilent(`yarn remove ${name}`, true);
80+
execSilent(`yarn cache clean`);
81+
execSilent(`yarn add ${tarballPath}`);
82+
} else {
83+
execSilent(`npm uninstall ${name}`);
84+
execSilent(`npm install ${tarballPath}`);
85+
}
86+
});
87+
break;
88+
case COMMANDS[1]: // link
89+
run(`Linking ${name} to ${targetPackagePath}`, () => {
90+
shell.cd(localPackagePath);
91+
execSilent('yarn link');
92+
shell.cd(targetPackagePath);
93+
execSilent(`yarn link ${name}`);
94+
});
95+
break;
96+
case COMMANDS[2]: // unlink
97+
run(`Unlinking ${name} from ${targetPackagePath}`, () => {
98+
shell.cd(targetPackagePath);
99+
execSilent(`yarn unlink ${name}`);
100+
shell.cd(localPackagePath);
101+
execSilent('yarn unlink');
102+
});
103+
break;
104+
default:
105+
showHelp();
106+
}
107+
}
108+
109+
main();

scripts/util.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
const shell = require('shelljs');
2+
require('shelljs/global');
3+
4+
const terminalCodes = {
5+
Red: '\\033[31m',
6+
LightGrey: '\\033[37m',
7+
Bold: '\\033[1m',
8+
ResetAll: '\\033[0m',
9+
};
10+
11+
module.exports = {
12+
run: (status, f) => {
13+
let result;
14+
shell.exec(`printf "🐎 ${status}..."`);
15+
const { LightGrey, Bold, ResetAll } = terminalCodes;
16+
try {
17+
result = f();
18+
} catch (e) {
19+
shell.exec(`printf "\\r❗️ ${status}...${Bold}${LightGrey}failed${ResetAll}\\n"`);
20+
shell.exec(`printf "${e.message}"`);
21+
process.exit(1);
22+
}
23+
shell.exec(`printf "\\r✅ ${status}...${Bold}${LightGrey}done${ResetAll}\\n"`);
24+
return result;
25+
},
26+
execSilent: (command, swallowError) => {
27+
const prevConfig = config.fatal;
28+
config.fatal = true;
29+
try {
30+
return shell.exec(command, { silent: true });
31+
} catch (e) {
32+
if (swallowError) {
33+
return;
34+
}
35+
throw e;
36+
} finally {
37+
config.fatal = prevConfig;
38+
}
39+
},
40+
terminalCodes,
41+
};

src/resolve/metadataResolver.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,9 +320,9 @@ const parseAsFolderMetadataXml =
320320
if (parts.length > 1) {
321321
const folderContentTypesDirs = getFolderContentTypeDirNames(registry);
322322
// check if the path contains a folder content name as a directory
323-
// e.g., `/reports/` and if it does return that folder name.
323+
const pathWithoutFile = parts.slice(0, -1);
324324
folderContentTypesDirs.some((dirName) => {
325-
if (fsPath.includes(`${sep}${dirName}${sep}`)) {
325+
if (pathWithoutFile.includes(dirName)) {
326326
folderName = dirName;
327327
}
328328
});

test/resolve/metadataResolver.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,31 @@ describe('MetadataResolver', () => {
398398
expect(comp[0]).to.have.deep.property('type', registryAccess.getTypeByName('ReportFolder'));
399399
});
400400

401+
it('should resolve folder metadata and reports from zip paths without leading directory', () => {
402+
const registryAccess = new RegistryAccess();
403+
const reportFolderDir = 'reports';
404+
const reportSubFolder = join(reportFolderDir, 'TestFolder');
405+
const virtualFS: VirtualDirectory[] = [
406+
{ dirPath: reportFolderDir, children: ['TestFolder-meta.xml', 'TestFolder'] },
407+
{ dirPath: reportSubFolder, children: ['MyReport.report-meta.xml'] },
408+
];
409+
const tree = new VirtualTreeContainer(virtualFS);
410+
const mdResolver = new MetadataResolver(registryAccess, tree);
411+
412+
const components = mdResolver.getComponentsFromPath(reportFolderDir);
413+
expect(components).to.be.an('array').with.lengthOf(2);
414+
415+
const folderComp = components.find((c) => c.type.name === 'ReportFolder');
416+
expect(folderComp).to.exist;
417+
expect(folderComp).to.have.property('name', 'TestFolder');
418+
expect(folderComp).to.have.deep.property('type', registryAccess.getTypeByName('ReportFolder'));
419+
420+
const reportComp = components.find((c) => c.type.name === 'Report');
421+
expect(reportComp).to.exist;
422+
expect(reportComp).to.have.property('name', 'TestFolder/MyReport');
423+
expect(reportComp).to.have.deep.property('type', registryAccess.getTypeByName('Report'));
424+
});
425+
401426
it('Should not mistake folder component of a mixed content type as that type', () => {
402427
// this test has coveage on non-mixedContent types as well by nature of the execution path
403428
const path = mixedContentInFolder.FOLDER_XML_PATH;

0 commit comments

Comments
 (0)