Skip to content
Open
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
20 changes: 18 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@ Supported package managers:
- `npm >=6`
- `yarn >=1 <2`

These and other package managers should work if you are bundling your extension.

### Override Prepublish Script

This section is optional. By default, `vsce` automatically detects and uses `npm`, `yarn`, `pnpm`, `vlt`, `deno` and `bun` to generate this the `run vscode:prepublish` command.

- Use `VSCE_RUN_PREPUBLISH="npm run vscode:prepublish"` or `vsce.runPrepublish: "npm run vscode:prepublish"` in your manifest file to override the auto-detected command.
- `vsce.runPrepublish: false` and `VSCE_RUN_PREPUBLISH=0` can disable the prepublish script. This is useful when you already transpiled your extension and you want to compile `.vsix` faster.

## Configuration

You can configure the behavior of `vsce` by using CLI flags (run `vsce --help` to list them all). Example:
Expand All @@ -52,12 +61,17 @@ Or you can also set them in the `package.json`, so that you avoid having to rety
{
"vsce": {
"baseImagesUrl": "https://my.custom/base/images/url",
"dependencies": true,
"runPrepublish": "pnpm run vscode:prepublish",
"dependencies": false,
"yarn": false
}
},
"packageManager": "pnpm@11.0.0", // optional
}
```

> **Note:** If you are bundling your extension, always set the `dependencies` option to `false`.
In newer version of `vsce` this option is set to `false` automatically, but providing it can make things a bit faster. Same applies to the `yarn` option.

## Development

First clone this repository, then:
Expand All @@ -79,4 +93,6 @@ Tests can be executed with:
$ npm test
```

Use `VSCE_DEBUG=1` to enable debug output.

> **Note:** [Yarn](https://www.npmjs.com/package/yarn) is required to run the tests.
22 changes: 19 additions & 3 deletions src/api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { publish as _publish, IPublishOptions, unpublish as _unpublish, IUnpublishOptions } from './publish';
import { packageCommand, listFiles as _listFiles, IPackageOptions } from './package';
import { packageCommand, listFiles as _listFiles, IPackageOptions, readManifest } from './package';
import { detectPackageManager } from './npm';

/**
* @deprecated prefer IPackageOptions instead
Expand Down Expand Up @@ -80,12 +81,27 @@ export function publish(options: IPublishOptions = {}): Promise<any> {
* Lists the files included in the extension's package.
* @public
*/
export function listFiles(options: IListFilesOptions = {}): Promise<string[]> {
export async function listFiles(options: IListFilesOptions = {}): Promise<string[]> {
const cwd = options.cwd || process.cwd();
const manifest = await readManifest(cwd);

let pm: string | null;
switch (options.packageManager) {
case PackageManager.Yarn: pm = 'yarn'; break;
case PackageManager.Npm: pm = 'npm'; break;
case PackageManager.None: pm = null; break;

case undefined:
default:
// cares all: detect prepublish script launcher
pm = await detectPackageManager(cwd, manifest, false); break;
}

return _listFiles({
...options,
useYarn: options.packageManager === PackageManager.Yarn,
dependencies: options.packageManager !== PackageManager.None,
});
}, pm);
}

/**
Expand Down
6 changes: 3 additions & 3 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ module.exports = function (argv: string[]): void {
)
.option('--ignoreFile <path>', 'Indicate alternative .vscodeignore')
// default must remain undefined for dependencies or we will fail to load defaults from package.json
.option('--dependencies', 'Enable dependency detection via npm or yarn', undefined)
.option('--dependencies', 'Enable dependency detection via npm or yarn. Never enable this option if you are bundling your extension', undefined)
.option('--no-dependencies', 'Disable dependency detection via npm or yarn', undefined)
.option('--readme-path <path>', 'Path to README file (defaults to README.md)')
.option('--follow-symlinks', 'Recurse into symlinked directories instead of treating them as files')
Expand Down Expand Up @@ -118,7 +118,7 @@ module.exports = function (argv: string[]): void {
.option('--no-gitHubIssueLinking', 'Disable automatic expansion of GitHub-style issue syntax into links')
.option('--no-gitLabIssueLinking', 'Disable automatic expansion of GitLab-style issue syntax into links')
// default must remain undefined for dependencies or we will fail to load defaults from package.json
.option('--dependencies', 'Enable dependency detection via npm or yarn', undefined)
.option('--dependencies', 'Enable dependency detection via npm or yarn. Never enable this option if you are bundling your extension', undefined)
.option('--no-dependencies', 'Disable dependency detection via npm or yarn', undefined)
.option('--pre-release', 'Mark this package as a pre-release')
.option('--allow-star-activation', 'Allow using * in activation events')
Expand Down Expand Up @@ -244,7 +244,7 @@ module.exports = function (argv: string[]): void {
.option('--allow-package-env-file', 'Allow packaging .env files')
.option('--ignoreFile <path>', 'Indicate alternative .vscodeignore')
// default must remain undefined for dependencies or we will fail to load defaults from package.json
.option('--dependencies', 'Enable dependency detection via npm or yarn', undefined)
.option('--dependencies', 'Enable dependency detection via npm or yarn. Never enable this option if you are bundling your extension', undefined)
.option('--no-dependencies', 'Disable dependency detection via npm or yarn', undefined)
.option('--pre-release', 'Mark this package as a pre-release')
.option('--allow-star-activation', 'Allow using * in activation events')
Expand Down
5 changes: 5 additions & 0 deletions src/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ export interface ManifestPackage {
private?: boolean;
pricing?: string;
files?: string[];
packageManager?: string;
devEngines?: { packageManager?: {
name?: string; version?: string;
onFail: "error" | "warn" | "ignore"
} };

// vsce
vsce?: any;
Expand Down
76 changes: 66 additions & 10 deletions src/npm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as fs from 'fs';
import * as cp from 'child_process';
import parseSemver from 'parse-semver';
import { CancellationToken, log, nonnull } from './util';
import type { ManifestPackage } from './manifest';

const exists = (file: string) =>
fs.promises.stat(file).then(
Expand Down Expand Up @@ -195,28 +196,83 @@ async function getYarnDependencies(cwd: string, packagedDependencies?: string[])
return [...result];
}

export async function detectYarn(cwd: string): Promise<boolean> {
for (const name of ['yarn.lock', '.yarnrc', '.yarnrc.yaml', '.pnp.cjs', '.yarn']) {
if (await exists(path.join(cwd, name))) {
/**
* Check if the package manager can be used for unbundled extensions.
*/
export function canNotBeUnbundled(pm: string | null): boolean {
return pm !== 'npm' && pm !== 'yarn1';
}

const YARN = [
{ name: 'yarn', files: ['.yarnrc.yaml', '.pnp.cjs', '.yarn/releases'] },
{ name: 'yarn1', files: ['.yarnrc', 'yarn.lock'] },
] as const

const MANAGERS = [
...YARN,
{ name: 'pnpm', files: ['pnpm-lock.yaml', 'pnpm-workspace.yaml', '.pnpmfile.cjs'] },
{ name: 'bun', files: ['bun.lock', 'bunfig.toml', 'bun.lockb'] },
{ name: 'vlt', files: ['vlt-lock.json', '.vltrc'] },
{ name: 'deno', files: ['deno.lock', 'deno.json', 'deno.jsonc'] },
{ name: 'npm', files: ['package.json', 'package-lock.json'] },
] as const;

export type ManagerName = (typeof MANAGERS)[number]['name'];

export async function detectPackageManager(cwd: string, manifest: ManifestPackage, useYarn: boolean | undefined, care?: ManagerName[]): Promise<string | null> {
const m = useYarn ? YARN : care ? MANAGERS.filter(({name}) => care.includes(name)) : MANAGERS;
for (const { name } of m) {
if (
manifest?.devEngines?.packageManager?.name === name ||
manifest?.packageManager?.startsWith(`${name}@`)
) {
if (process.env['VSCE_DEBUG']) console.log('Package manager:', name);
return name;
}
}

for (const mgr of m) {
for (const filename of mgr.files) {
if (!await exists(path.join(cwd, filename))) {
continue;
}
if (!process.env['VSCE_TESTS']) {
log.info(
`Detected presence of ${name}. Using 'yarn' instead of 'npm' (to override this pass '--no-yarn' on the command line).`
);
const suffix = mgr.name === 'yarn'
? " instead of 'npm' (to override this pass '--no-yarn' on the command line)."
: ' logic.';
log.info(`Detected presence of ${filename}. Using '${mgr.name}'${suffix}`);
}
return true;
if (process.env['VSCE_DEBUG']) console.log('Package manager:', mgr.name);
return mgr.name;
}
}
return false;

if (process.env['VSCE_DEBUG']) console.log('Package manager: null');
return null;
}

export async function getPrepublishCommand(manifest: ManifestPackage, pm: string | null): Promise<string> {
const envv = process.env['VSCE_RUN_PREPUBLISH']
if (envv === "" || envv === "0" || manifest?.vsce?.runPrepublish === false) return "";
const customCommand = envv || manifest?.vsce?.runPrepublish;
if (customCommand) {
return customCommand;
}

if (pm === null) return 'npm run vscode:prepublish';
if (pm === 'deno') return 'npm run vscode:prepublish';
if (pm === 'yarn1') return 'yarn run vscode:prepublish';
return pm + ' run vscode:prepublish'; // known pms
}

export async function getDependencies(
cwd: string,
dependencies: 'npm' | 'yarn' | 'none' | undefined,
dependencies: 'npm' | 'yarn' | 'none',
packagedDependencies?: string[]
): Promise<string[]> {
if (dependencies === 'none') {
return [cwd];
} else if (dependencies === 'yarn' || (dependencies === undefined && (await detectYarn(cwd)))) {
} else if (dependencies === 'yarn') {
return await getYarnDependencies(cwd, packagedDependencies);
} else {
return await getNpmDependencies(cwd);
Expand Down
Loading