Skip to content

Commit ba66818

Browse files
authored
Merge pull request #1050 from microsoft/benibenj/narrow-smelt
Fix unused-files-patterns check and add tests
2 parents 3090717 + 0e9cb5a commit ba66818

File tree

6 files changed

+129
-6
lines changed

6 files changed

+129
-6
lines changed

src/package.ts

+19-4
Original file line numberDiff line numberDiff line change
@@ -1968,7 +1968,7 @@ export async function ls(options: ILSOptions = {}): Promise<void> {
19681968
if (options.tree) {
19691969
const printableFileStructure = await util.generateFileStructureTree(
19701970
getDefaultPackageName(manifest, options),
1971-
files.map(f => ({ origin: f, tree: f }))
1971+
files.map(f => ({ origin: path.join(cwd, f), tree: f }))
19721972
);
19731973
console.log(printableFileStructure.join('\n'));
19741974
} else {
@@ -2011,8 +2011,23 @@ export async function printAndValidatePackagedFiles(files: IFile[], cwd: string,
20112011
// Throw an error if the extension uses the files property in package.json and
20122012
// the package does not include at least one file for each include pattern
20132013
else if (manifest.files !== undefined && manifest.files.length > 0 && !options.allowUnusedFilesPattern) {
2014-
const originalFilePaths = files.map(f => util.vsixPathToFilePath(f.path));
2015-
const unusedIncludePatterns = manifest.files.filter(includePattern => !originalFilePaths.some(filePath => minimatch(filePath, includePattern, MinimatchOptions)));
2014+
const localPaths = (files.filter(f => !isInMemoryFile(f)) as ILocalFile[]).map(f => util.normalize(f.localPath));
2015+
const filesIncludePatterns = manifest.files.map(includePattern => util.normalize(path.join(cwd, includePattern)));
2016+
2017+
const unusedIncludePatterns = filesIncludePatterns.filter(includePattern => {
2018+
// Check if the pattern provided by the user matches any file in the package
2019+
if (localPaths.some(localFilePath => minimatch(localFilePath, includePattern, MinimatchOptions))) {
2020+
return false;
2021+
}
2022+
// Check if the pattern provided by the user matches any folder in the package
2023+
if (!/(^|\/)[^/]*\*[^/]*$/.test(includePattern)) {
2024+
includePattern = (/\/$/.test(includePattern) ? `${includePattern}**` : `${includePattern}/**`);
2025+
return !localPaths.some(localFilePath => minimatch(localFilePath, includePattern, MinimatchOptions));
2026+
}
2027+
// Pattern does not match any file or folder
2028+
return true;
2029+
});
2030+
20162031
if (unusedIncludePatterns.length > 0) {
20172032
let message = '';
20182033
message += `The following include patterns in the ${chalk.bold('"files"')} property in package.json do not match any files packaged in the extension:\n`;
@@ -2030,7 +2045,7 @@ export async function printAndValidatePackagedFiles(files: IFile[], cwd: string,
20302045
getDefaultPackageName(manifest, options),
20312046
files.map(f => ({
20322047
// File path relative to the extension root
2033-
origin: util.vsixPathToFilePath(f.path),
2048+
origin: !isInMemoryFile(f) ? f.localPath : util.vsixPathToFilePath(f.path),
20342049
// File path in the VSIX
20352050
tree: f.path
20362051
})),
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
LICENSE...
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Some text in here!
2+
Some text in here!
3+
Some text in here!
4+
Some text in here!
5+
Some text in here!
6+
Some text in here!
7+
Some text in here!
8+
Some text in here!
9+
Some text in here!
10+
Some text in here!
11+
Some text in here!
12+
Some text in here!

src/test/fixtures/manifestFiles/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
"publisher": "joaomoreno",
44
"version": "1.0.0",
55
"engines": { "vscode": "*" },
6-
"files": ["foo", "foo2/bar2/include.me", "*/bar3/**", "package.json"]
6+
"files": ["foo", "foo2/bar2/include.me", "*/bar3/**", "package.json", "LICENSE"]
77
}

src/test/package.test.ts

+92-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
versionBump,
1515
VSIX,
1616
LicenseProcessor,
17+
printAndValidatePackagedFiles,
1718
} from '../package';
1819
import { ManifestPackage } from '../manifest';
1920
import * as path from 'path';
@@ -88,6 +89,63 @@ function createManifest(extra: Partial<ManifestPackage> = {}): ManifestPackage {
8889
};
8990
}
9091

92+
const PROCESS_ERROR_MESSAGE = 'PROCESS ERROR';
93+
async function testPrintAndValidatePackagedFiles(files: IFile[], cwd: string, manifest: ManifestPackage, options: IPackageOptions, errorExpected: boolean, warningExpected: boolean): Promise<void> {
94+
const originalLogError = log.error;
95+
const originalLogWarn = log.warn;
96+
const originalProcessExit = process.exit;
97+
const warns: string[] = [];
98+
const errors: string[] = [];
99+
let exited = false;
100+
let errorThrown: string | undefined;
101+
log.error = (message: string) => errors.push(message);
102+
log.warn = (message: string) => warns.push(message);
103+
process.exit = (() => { exited = true; throw Error(PROCESS_ERROR_MESSAGE); }) as () => never;
104+
105+
try {
106+
await printAndValidatePackagedFiles(files, cwd, manifest, options);
107+
} catch (e: any) {
108+
if (e instanceof Error && e.message !== PROCESS_ERROR_MESSAGE) {
109+
errorThrown = e.message + '\n' + e.stack;
110+
}
111+
} finally {
112+
process.exit = originalProcessExit;
113+
log.error = originalLogError;
114+
log.warn = originalLogWarn;
115+
}
116+
117+
// Validate that the correct number of errors and warnings were thrown
118+
const messages = [];
119+
120+
if (errorExpected !== !!errors.length) {
121+
if (errors.length) {
122+
messages.push(...errors);
123+
} else {
124+
messages.push('Expected an error');
125+
}
126+
}
127+
128+
if (warningExpected !== !!warns.length) {
129+
if (warns.length) {
130+
messages.push(...warns);
131+
} else {
132+
messages.push('Expected a warning');
133+
}
134+
}
135+
136+
if (!errorExpected && exited) {
137+
messages.push('Process exited');
138+
}
139+
140+
if (!errorExpected && !!errorThrown && !exited) {
141+
messages.push('Error thrown: ' + errorThrown);
142+
}
143+
144+
if (messages.length) {
145+
throw new Error(messages.join('\n'));
146+
}
147+
}
148+
91149
describe('collect', function () {
92150
this.timeout(60000);
93151

@@ -133,22 +191,55 @@ describe('collect', function () {
133191
]);
134192
});
135193

136-
it('should include content of manifest.files', async () => {
194+
it('manifest.files', async () => {
137195
const cwd = fixture('manifestFiles');
138196
const manifest = await readManifest(cwd);
139197
const files = await collect(manifest, { cwd });
140198
const names = files.map(f => f.path).sort();
141199

200+
await testPrintAndValidatePackagedFiles(files, cwd, manifest, {}, false, false);
201+
142202
assert.deepStrictEqual(names, [
143203
'[Content_Types].xml',
144204
'extension.vsixmanifest',
205+
'extension/LICENSE.txt',
145206
'extension/foo/bar/hello.txt',
146207
'extension/foo2/bar2/include.me',
147208
'extension/foo3/bar3/hello.txt',
148209
'extension/package.json',
149210
]);
150211
});
151212

213+
it('manifest.files unused-files-patterns check 1', async () => {
214+
const cwd = fixture('manifestFiles');
215+
const manifest = await readManifest(cwd);
216+
217+
const manifestCopy = { ...manifest, files: [...manifest.files ?? [], 'extension/foo/bar/bye.txt'] };
218+
const files = await collect(manifestCopy, { cwd });
219+
220+
await testPrintAndValidatePackagedFiles(files, cwd, manifestCopy, {}, true, false);
221+
});
222+
223+
it('manifest.files unused-files-patterns check 2', async () => {
224+
const cwd = fixture('manifestFiles');
225+
const manifest = await readManifest(cwd);
226+
227+
const manifestCopy = { ...manifest, files: [...manifest.files ?? [], 'extension/fo'] };
228+
const files = await collect(manifestCopy, { cwd });
229+
230+
await testPrintAndValidatePackagedFiles(files, cwd, manifestCopy, {}, true, false);
231+
});
232+
233+
it('manifest.files unused-files-patterns check 3', async () => {
234+
const cwd = fixture('manifestFiles');
235+
const manifest = await readManifest(cwd);
236+
237+
const manifestCopy = { ...manifest, files: ['**'] };
238+
const files = await collect(manifestCopy, { cwd });
239+
240+
await testPrintAndValidatePackagedFiles(files, cwd, manifestCopy, {}, false, false);
241+
});
242+
152243
it('should ignore devDependencies', () => {
153244
const cwd = fixture('devDependencies');
154245
return readManifest(cwd)

src/util.ts

+4
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,10 @@ export async function generateFileStructureTree(rootFolder: string, filePaths: {
284284
const parts = filePath.split('/');
285285
let currentLevel = folderTree;
286286
parts.forEach(part => {
287+
if (currentLevel === undefined) {
288+
throw new Error(`currentLevel is undefined for ${part} in ${filePath}`);
289+
}
290+
287291
if (typeof currentLevel[part] === 'number') {
288292
currentLevel[part] = size;
289293
} else if (currentLevel[part]) {

0 commit comments

Comments
 (0)