diff --git a/package-lock.json b/package-lock.json index d01cf083..9bee93a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -942,8 +942,9 @@ }, "ignore": { "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==" + "resolved": "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha1-8VCotQo0KJsz4i9YiavU2AFvDlc= sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true }, "import-fresh": { "version": "3.2.1", diff --git a/package.json b/package.json index 1ee1df7a..b9682c4c 100644 --- a/package.json +++ b/package.json @@ -42,11 +42,11 @@ "commander": "^6.1.0", "denodeify": "^1.2.1", "glob": "^7.0.6", - "ignore": "^5.1.8", "leven": "^3.1.0", "lodash": "^4.17.15", "markdown-it": "^10.0.0", "mime": "^1.3.4", + "minimatch": "^3.0.3", "osenv": "^0.1.3", "parse-semver": "^1.1.1", "read": "^1.0.7", @@ -64,6 +64,7 @@ "@types/lodash": "^4.14.123", "@types/markdown-it": "0.0.2", "@types/mime": "^1", + "@types/minimatch": "^3.0.3", "@types/mocha": "^7.0.2", "@types/node": "^10.17.60", "@types/read": "^0.0.28", diff --git a/src/package.ts b/src/package.ts index d54d8ebc..84963c18 100644 --- a/src/package.ts +++ b/src/package.ts @@ -7,6 +7,7 @@ import { ExtensionKind, Manifest } from './manifest'; import { ITranslations, patchNLS } from './nls'; import * as util from './util'; import * as _glob from 'glob'; +import * as minimatch from 'minimatch'; import * as denodeify from 'denodeify'; import * as markdownit from 'markdown-it'; import * as cheerio from 'cheerio'; @@ -22,7 +23,6 @@ import { validateVSCodeTypesCompatibility, } from './validation'; import { detectYarn, getDependencies } from './npm'; -import ignore from 'ignore'; const readFile = denodeify(fs.readFile); const unlink = denodeify(fs.unlink as any); @@ -37,6 +37,8 @@ const resourcesPath = path.join(path.dirname(__dirname), 'resources'); const vsixManifestTemplatePath = path.join(resourcesPath, 'extension.vsixmanifest'); const contentTypesTemplatePath = path.join(resourcesPath, '[Content_Types].xml'); +const MinimatchOptions: minimatch.IOptions = { dot: true }; + export interface IInMemoryFile { path: string; mode?: number; @@ -1118,28 +1120,53 @@ function collectAllFiles(cwd: string, useYarn?: boolean, dependencyEntryPoints?: }); } -async function readIgnoreFile(cwd: string, ignoreFile?: string): Promise { - try { - return await readFile(ignoreFile ? ignoreFile : path.join(cwd, '.vscodeignore'), 'utf8'); - } catch (err) { - if (err.code !== 'ENOENT' || ignoreFile) { - throw err; - } - - return ''; - } -} - -async function collectFiles( +function collectFiles( cwd: string, useYarn?: boolean, dependencyEntryPoints?: string[], ignoreFile?: string ): Promise { - const files = (await collectAllFiles(cwd, useYarn, dependencyEntryPoints)).filter(f => !/\r$/m.test(f)); - const rawIgnore = await readIgnoreFile(cwd, ignoreFile); - - return ignore().add(defaultIgnore).add(rawIgnore).add(notIgnored).filter(files); + return collectAllFiles(cwd, useYarn, dependencyEntryPoints).then(files => { + files = files.filter(f => !/\r$/m.test(f)); + + return ( + readFile(ignoreFile ? ignoreFile : path.join(cwd, '.vscodeignore'), 'utf8') + .catch(err => + err.code !== 'ENOENT' ? Promise.reject(err) : ignoreFile ? Promise.reject(err) : Promise.resolve('') + ) + + // Parse raw ignore by splitting output into lines and filtering out empty lines and comments + .then(rawIgnore => + rawIgnore + .split(/[\n\r]/) + .map(s => s.trim()) + .filter(s => !!s) + .filter(i => !/^\s*#/.test(i)) + ) + + // Add '/**' to possible folder names + .then(ignore => [ + ...ignore, + ...ignore.filter(i => !/(^|\/)[^/]*\*[^/]*$/.test(i)).map(i => (/\/$/.test(i) ? `${i}**` : `${i}/**`)), + ]) + + // Combine with default ignore list + .then(ignore => [...defaultIgnore, ...ignore, ...notIgnored]) + + // Split into ignore and negate list + .then(ignore => _.partition(ignore, i => !/^\s*!/.test(i))) + .then(r => ({ ignore: r[0], negate: r[1] })) + + // Filter out files + .then(({ ignore, negate }) => + files.filter( + f => + !ignore.some(i => minimatch(f, i, MinimatchOptions)) || + negate.some(i => minimatch(f, i.substr(1), MinimatchOptions)) + ) + ) + ); + }); } export function processFiles(processors: IProcessor[], files: IFile[]): Promise { diff --git a/src/test/package.test.ts b/src/test/package.test.ts index fe31ec76..8f199b81 100644 --- a/src/test/package.test.ts +++ b/src/test/package.test.ts @@ -156,7 +156,12 @@ describe('collect', function () { const files = await collect(manifest, { cwd }); const names = files.map(f => f.path).sort(); - assert.deepStrictEqual(names, ['[Content_Types].xml', 'extension.vsixmanifest', 'extension/package.json']); + assert.deepStrictEqual(names, [ + '[Content_Types].xml', + 'extension.vsixmanifest', + 'extension/foo/bar/hello.txt', + 'extension/package.json', + ]); }); it('should ignore devDependencies', () => {