Skip to content

Commit 51e122a

Browse files
committed
Make follow-symlinks optional
This new behavior fixes packaging with symlinks to directories in extensions. We make it optional in case someone wants to keep the intermediate behavior.
1 parent 3cd6ca0 commit 51e122a

File tree

3 files changed

+33
-12
lines changed

3 files changed

+33
-12
lines changed

src/main.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,9 @@ module.exports = function (argv: string[]): void {
7575
.option('--dependencies', 'Enable dependency detection via npm or yarn', undefined)
7676
.option('--no-dependencies', 'Disable dependency detection via npm or yarn', undefined)
7777
.option('--readme-path <path>', 'Path to README file (defaults to README.md)')
78-
.action(({ tree, yarn, packagedDependencies, ignoreFile, dependencies, readmePath }) =>
79-
main(ls({ tree, useYarn: yarn, packagedDependencies, ignoreFile, dependencies, readmePath }))
78+
.option('--follow-symlinks', 'Recurse into symlinked directories instead of treating them as files')
79+
.action(({ tree, yarn, packagedDependencies, ignoreFile, dependencies, readmePath, followSymlinks }) =>
80+
main(ls({ tree, useYarn: yarn, packagedDependencies, ignoreFile, dependencies, readmePath, followSymlinks }))
8081
);
8182

8283
program
@@ -119,6 +120,7 @@ module.exports = function (argv: string[]): void {
119120
.option('--allow-unused-files-pattern', 'Allow include patterns for the files field in package.json that does not match any file')
120121
.option('--skip-license', 'Allow packaging without license file')
121122
.option('--sign-tool <path>', 'Path to the VSIX signing tool. Will be invoked with two arguments: `SIGNTOOL <path/to/extension.signature.manifest> <path/to/extension.signature.p7s>`.')
123+
.option('--follow-symlinks', 'Recurse into symlinked directories instead of treating them as files')
122124
.action(
123125
(
124126
version,
@@ -147,6 +149,7 @@ module.exports = function (argv: string[]): void {
147149
allowUnusedFilesPattern,
148150
skipLicense,
149151
signTool,
152+
followSymlinks,
150153
}
151154
) =>
152155
main(
@@ -176,6 +179,7 @@ module.exports = function (argv: string[]): void {
176179
allowUnusedFilesPattern,
177180
skipLicense,
178181
signTool,
182+
followSymlinks,
179183
})
180184
)
181185
);
@@ -229,6 +233,7 @@ module.exports = function (argv: string[]): void {
229233
.option('--allow-unused-files-pattern', 'Allow include patterns for the files field in package.json that does not match any file')
230234
.option('--skip-duplicate', 'Fail silently if version already exists on the marketplace')
231235
.option('--skip-license', 'Allow publishing without license file')
236+
.option('--follow-symlinks', 'Recurse into symlinked directories instead of treating them as files')
232237
.action(
233238
(
234239
version,
@@ -263,6 +268,7 @@ module.exports = function (argv: string[]): void {
263268
skipDuplicate,
264269
skipLicense,
265270
signTool,
271+
followSymlinks,
266272
}
267273
) =>
268274
main(
@@ -297,7 +303,8 @@ module.exports = function (argv: string[]): void {
297303
allowUnusedFilesPattern,
298304
skipDuplicate,
299305
skipLicense,
300-
signTool
306+
signTool,
307+
followSymlinks
301308
})
302309
)
303310
);

src/package.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,16 @@ export interface IPackageOptions {
9595
* `target` is set. For example, if `target` is `linux-x64` and there are
9696
* folders named `win32-x64`, `darwin-arm64` or `web`, the files inside
9797
* those folders will be ignored.
98-
*
98+
*
9999
* @default false
100100
*/
101101
readonly ignoreOtherTargetFolders?: boolean;
102102

103+
/**
104+
* Recurse into symlinked directories instead of treating them as files.
105+
*/
106+
readonly followSymlinks?: boolean;
107+
103108
readonly commitMessage?: string;
104109
readonly gitTagVersion?: boolean;
105110
readonly updatePackageJson?: boolean;
@@ -1613,11 +1618,12 @@ const defaultIgnore = [
16131618
async function collectAllFiles(
16141619
cwd: string,
16151620
dependencies: 'npm' | 'yarn' | 'none' | undefined,
1616-
dependencyEntryPoints?: string[]
1621+
dependencyEntryPoints?: string[],
1622+
followSymlinks:boolean = true
16171623
): Promise<string[]> {
16181624
const deps = await getDependencies(cwd, dependencies, dependencyEntryPoints);
16191625
const promises = deps.map(dep =>
1620-
glob('**', { cwd: dep, nodir: true, follow: true, dot: true, ignore: 'node_modules/**' }).then(files =>
1626+
glob('**', { cwd: dep, nodir: true, follow: followSymlinks, dot: true, ignore: 'node_modules/**' }).then(files =>
16211627
files.map(f => path.relative(cwd, path.join(dep, f))).map(f => f.replace(/\\/g, '/'))
16221628
)
16231629
);
@@ -1647,11 +1653,12 @@ function collectFiles(
16471653
ignoreFile?: string,
16481654
manifestFileIncludes?: string[],
16491655
readmePath?: string,
1656+
followSymlinks:boolean = false
16501657
): Promise<string[]> {
16511658
readmePath = readmePath ?? 'README.md';
16521659
const notIgnored = ['!package.json', `!${readmePath}`];
16531660

1654-
return collectAllFiles(cwd, dependencies, dependencyEntryPoints).then(files => {
1661+
return collectAllFiles(cwd, dependencies, dependencyEntryPoints, followSymlinks).then(files => {
16551662
files = files.filter(f => !/\r$/m.test(f));
16561663

16571664
return (
@@ -1666,7 +1673,7 @@ function collectFiles(
16661673
manifestFileIncludes ?
16671674
// include all files in manifestFileIncludes and ignore the rest
16681675
Promise.resolve(manifestFileIncludes.map(file => `!${file}`).concat(['**']).join('\n\r')) :
1669-
// "files" property not used in package.json
1676+
// "files" property not used in package.json
16701677
Promise.resolve('')
16711678
)
16721679

@@ -1758,7 +1765,7 @@ export function collect(manifest: ManifestPackage, options: IPackageOptions = {}
17581765
const ignoreFile = options.ignoreFile || undefined;
17591766
const processors = createDefaultProcessors(manifest, options);
17601767

1761-
return collectFiles(cwd, getDependenciesOption(options), packagedDependencies, ignoreFile, manifest.files, options.readmePath).then(fileNames => {
1768+
return collectFiles(cwd, getDependenciesOption(options), packagedDependencies, ignoreFile, manifest.files, options.readmePath, options.followSymlinks).then(fileNames => {
17621769
const files = fileNames.map(f => ({ path: util.filePathToVsixPath(f), localPath: path.join(cwd, f) }));
17631770

17641771
return processFiles(processors, files);
@@ -1931,6 +1938,7 @@ export interface IListFilesOptions {
19311938
readonly dependencies?: boolean;
19321939
readonly prepublish?: boolean;
19331940
readonly readmePath?: string;
1941+
readonly followSymlinks?: boolean;
19341942
}
19351943

19361944
/**
@@ -1944,7 +1952,7 @@ export async function listFiles(options: IListFilesOptions = {}): Promise<string
19441952
await prepublish(cwd, manifest, options.useYarn);
19451953
}
19461954

1947-
return await collectFiles(cwd, getDependenciesOption(options), options.packagedDependencies, options.ignoreFile, manifest.files, options.readmePath);
1955+
return await collectFiles(cwd, getDependenciesOption(options), options.packagedDependencies, options.ignoreFile, manifest.files, options.readmePath, options.followSymlinks);
19481956
}
19491957

19501958
interface ILSOptions {
@@ -1954,6 +1962,7 @@ interface ILSOptions {
19541962
readonly ignoreFile?: string;
19551963
readonly dependencies?: boolean;
19561964
readonly readmePath?: string;
1965+
readonly followSymlinks?: boolean;
19571966
}
19581967

19591968
/**
@@ -2008,7 +2017,7 @@ export async function printAndValidatePackagedFiles(files: IFile[], cwd: string,
20082017
util.log.error(message);
20092018
process.exit(1);
20102019
}
2011-
// Throw an error if the extension uses the files property in package.json and
2020+
// Throw an error if the extension uses the files property in package.json and
20122021
// the package does not include at least one file for each include pattern
20132022
else if (manifest.files !== undefined && manifest.files.length > 0 && !options.allowUnusedFilesPattern) {
20142023
const localPaths = (files.filter(f => !isInMemoryFile(f)) as ILocalFile[]).map(f => util.normalize(f.localPath));

src/publish.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ export interface IPublishOptions {
5858
readonly dependencyEntryPoints?: string[];
5959
readonly ignoreFile?: string;
6060

61+
/**
62+
* Recurse into symlinked directories instead of treating them as files
63+
*/
64+
readonly followSymlinks?: boolean;
65+
6166
/**
6267
* The Personal Access Token to use.
6368
*
@@ -352,4 +357,4 @@ export async function getPAT(publisher: string, options: IPublishOptions | IUnpu
352357
}
353358

354359
return (await getPublisher(publisher)).pat;
355-
}
360+
}

0 commit comments

Comments
 (0)