Added pnpm support. Sloppy and incomplete. Unsure of what to do

with the symlinks without modifying the project.

microsoft/vscode-vsce#421
This commit is contained in:
Empathic Qubit 2020-09-04 22:54:15 -04:00
parent 5ade7bfa42
commit f2c83c1bc3
7 changed files with 1445 additions and 25 deletions

View file

@ -25,11 +25,11 @@
}, },
"scripts": { "scripts": {
"copy-vsce": "mkdir -p out && cp src/vsce out/vsce", "copy-vsce": "mkdir -p out && cp src/vsce out/vsce",
"compile": "tsc && yarn copy-vsce", "compile": "tsc && npm run copy-vsce",
"watch": "yarn copy-vsce && tsc --watch", "watch": "npm run copy-vsce && tsc --watch",
"watch-test": "yarn copy-vsce && concurrently \"tsc --watch\" \"mocha --watch\"", "watch-test": "npm run copy-vsce && concurrently \"tsc --watch\" \"mocha --watch\"",
"test": "mocha", "test": "mocha",
"prepublishOnly": "tsc && yarn copy-vsce && mocha", "prepublishOnly": "tsc && npm run copy-vsce && mocha",
"vsce": "out/vsce" "vsce": "out/vsce"
}, },
"engines": { "engines": {

1301
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -30,6 +30,11 @@ export interface ICreateVSIXOptions {
* Should use Yarn instead of NPM. * Should use Yarn instead of NPM.
*/ */
useYarn?: boolean; useYarn?: boolean;
/**
* Select the package manager to use
*/
usePackageManager?: "yarn" | "pnpm" | "npm";
} }
export interface IPublishOptions { export interface IPublishOptions {
@ -62,6 +67,11 @@ export interface IPublishOptions {
* Should use Yarn instead of NPM. * Should use Yarn instead of NPM.
*/ */
useYarn?: boolean; useYarn?: boolean;
/**
* Select the package manager to use
*/
usePackageManager?: "yarn" | "pnpm" | "npm";
} }
/** /**
@ -69,7 +79,8 @@ export interface IPublishOptions {
*/ */
export enum PackageManager { export enum PackageManager {
Npm, Npm,
Yarn Yarn,
Pnpm,
} }
export interface IListFilesOptions { export interface IListFilesOptions {
@ -122,6 +133,11 @@ export interface IPublishVSIXOptions {
* Should use Yarn instead of NPM. * Should use Yarn instead of NPM.
*/ */
useYarn?: boolean; useYarn?: boolean;
/**
* Select the package manager to use
*/
usePackageManager?: "yarn" | "pnpm" | "npm";
} }
/** /**
@ -142,7 +158,7 @@ export function publish(options: IPublishOptions = {}): Promise<any> {
* Lists the files included in the extension's package. * Lists the files included in the extension's package.
*/ */
export function listFiles(options: IListFilesOptions = {}): Promise<string[]> { export function listFiles(options: IListFilesOptions = {}): Promise<string[]> {
return _listFiles(options.cwd, options.packageManager === PackageManager.Yarn, options.packagedDependencies, options.ignoreFile); return _listFiles(options.cwd, options.packageManager === PackageManager.Yarn, <any>({ [PackageManager.Yarn]: "yarn", [PackageManager.Npm]: "npm", [PackageManager.Pnpm]: "pnpm" }[options.packageManager]), options.packagedDependencies, options.ignoreFile);
} }
/** /**

View file

@ -62,9 +62,10 @@ module.exports = function (argv: string[]): void {
.command('ls') .command('ls')
.description('Lists all the files that will be published') .description('Lists all the files that will be published')
.option('--yarn', 'Use yarn instead of npm') .option('--yarn', 'Use yarn instead of npm')
.option('--packageManager [yarn|npm|pnpm]', 'Use yarn, npm, or pnpm package manager')
.option('--packagedDependencies <path>', 'Select packages that should be published only (includes dependencies)', (val, all) => all ? all.concat(val) : [val], undefined) .option('--packagedDependencies <path>', 'Select packages that should be published only (includes dependencies)', (val, all) => all ? all.concat(val) : [val], undefined)
.option('--ignoreFile [path]', 'Indicate alternative .vscodeignore') .option('--ignoreFile [path]', 'Indicate alternative .vscodeignore')
.action(({ yarn, packagedDependencies, ignoreFile }) => main(ls(undefined, yarn, packagedDependencies, ignoreFile))); .action(({ yarn, packagedDependencies, ignoreFile, packageManager }) => main(ls(undefined, yarn, packagedDependencies, ignoreFile, packageManager)));
program program
.command('package') .command('package')
@ -74,9 +75,10 @@ module.exports = function (argv: string[]): void {
.option('--baseContentUrl [url]', 'Prepend all relative links in README.md with this url.') .option('--baseContentUrl [url]', 'Prepend all relative links in README.md with this url.')
.option('--baseImagesUrl [url]', 'Prepend all relative image links in README.md with this url.') .option('--baseImagesUrl [url]', 'Prepend all relative image links in README.md with this url.')
.option('--yarn', 'Use yarn instead of npm') .option('--yarn', 'Use yarn instead of npm')
.option('--packageManager [yarn|npm|pnpm]', 'Use yarn, npm, or pnpm package manager')
.option('--ignoreFile [path]', 'Indicate alternative .vscodeignore') .option('--ignoreFile [path]', 'Indicate alternative .vscodeignore')
.option('--noGitHubIssueLinking', 'Prevent automatic expansion of GitHub-style issue syntax into links') .option('--noGitHubIssueLinking', 'Prevent automatic expansion of GitHub-style issue syntax into links')
.action(({ out, githubBranch, baseContentUrl, baseImagesUrl, yarn, ignoreFile, noGitHubIssueLinking }) => main(packageCommand({ packagePath: out, githubBranch, baseContentUrl, baseImagesUrl, useYarn: yarn, ignoreFile, expandGitHubIssueLinks: noGitHubIssueLinking }))); .action(({ out, githubBranch, baseContentUrl, baseImagesUrl, yarn, ignoreFile, noGitHubIssueLinking, packageManager }) => main(packageCommand({ packagePath: out, githubBranch, baseContentUrl, baseImagesUrl, useYarn: yarn, ignoreFile, expandGitHubIssueLinks: noGitHubIssueLinking, usePackageManager: packageManager })));
program program
.command('publish [<version>]') .command('publish [<version>]')
@ -88,10 +90,11 @@ module.exports = function (argv: string[]): void {
.option('--baseContentUrl [url]', 'Prepend all relative links in README.md with this url.') .option('--baseContentUrl [url]', 'Prepend all relative links in README.md with this url.')
.option('--baseImagesUrl [url]', 'Prepend all relative image links in README.md with this url.') .option('--baseImagesUrl [url]', 'Prepend all relative image links in README.md with this url.')
.option('--yarn', 'Use yarn instead of npm while packing extension files') .option('--yarn', 'Use yarn instead of npm while packing extension files')
.option('--packageManager [yarn|npm|pnpm]', 'Use yarn, npm, or pnpm package manager')
.option('--noVerify') .option('--noVerify')
.option('--ignoreFile [path]', 'Indicate alternative .vscodeignore') .option('--ignoreFile [path]', 'Indicate alternative .vscodeignore')
.option('--web', 'Experimental flag to enable publishing web extensions. Note: This is supported only for selected extensions.') .option('--web', 'Experimental flag to enable publishing web extensions. Note: This is supported only for selected extensions.')
.action((version, { pat, message, packagePath, githubBranch, baseContentUrl, baseImagesUrl, yarn, noVerify, ignoreFile, web }) => main(publish({ pat, commitMessage: message, version, packagePath, githubBranch, baseContentUrl, baseImagesUrl, useYarn: yarn, noVerify, ignoreFile, web }))); .action((version, { pat, message, packagePath, githubBranch, baseContentUrl, baseImagesUrl, yarn, noVerify, ignoreFile, web, packageManager }) => main(publish({ pat, commitMessage: message, version, packagePath, githubBranch, baseContentUrl, baseImagesUrl, useYarn: yarn, noVerify, ignoreFile, web, usePackageManager: packageManager })));
program program
.command('unpublish [<extensionid>]') .command('unpublish [<extensionid>]')

View file

@ -1,6 +1,7 @@
import * as path from 'path'; import * as path from 'path';
import * as fs from 'fs'; import * as fs from 'fs';
import * as cp from 'child_process'; import * as cp from 'child_process';
import * as denodeify from 'denodeify';
import * as parseSemver from 'parse-semver'; import * as parseSemver from 'parse-semver';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { CancellationToken } from './util'; import { CancellationToken } from './util';
@ -71,6 +72,13 @@ export interface YarnDependency {
children: YarnDependency[]; children: YarnDependency[];
} }
interface PnpmTreeNode {
name: string;
from: string;
version: string;
dependencies: { [key:string]:PnpmTreeNode };
}
function asYarnDependency(prefix: string, tree: YarnTreeNode, prune: boolean): YarnDependency | null { function asYarnDependency(prefix: string, tree: YarnTreeNode, prune: boolean): YarnDependency | null {
if (prune && /@[\^~]/.test(tree.name)) { if (prune && /@[\^~]/.test(tree.name)) {
return null; return null;
@ -99,6 +107,32 @@ function asYarnDependency(prefix: string, tree: YarnTreeNode, prune: boolean): Y
return { name, path: dependencyPath, children }; return { name, path: dependencyPath, children };
} }
async function asPnpmDependency(prefix: string, tree: PnpmTreeNode, prune: boolean): Promise<YarnDependency | null> {
if (prune && /^[\^~]/.test(tree.version)) {
return null;
}
let name = tree.name || tree.from;
let s = path.sep;
const dependencyPath = await denodeify(fs.realpath)(`${prefix}${s}${name}`);
const children: YarnDependency[] = [];
console.log(dependencyPath);
const deps = await Promise.all(
_.values(tree.dependencies || {})
.map(child => asPnpmDependency(`${dependencyPath}${s}..`, child, prune))
);
for(const dep of deps) {
if (dep) {
children.push(dep);
}
}
return { name, path: dependencyPath, children };
}
function selectYarnDependencies(deps: YarnDependency[], packagedDependencies: string[]): YarnDependency[] { function selectYarnDependencies(deps: YarnDependency[], packagedDependencies: string[]): YarnDependency[] {
const index = new class { const index = new class {
@ -145,6 +179,30 @@ function selectYarnDependencies(deps: YarnDependency[], packagedDependencies: st
return reached.values; return reached.values;
} }
async function getPnpmProductionDependencies(cwd: string, packagedDependencies?: string[]): Promise<YarnDependency[]> {
const raw = await new Promise<string>((c, e) => cp.exec('pnpm list --depth 1000000 --prod --json --silent', { cwd, encoding: 'utf8', env: { ...process.env }, maxBuffer: 5000 * 1024 }, (err, stdout) => err ? e(err) : c(stdout)));
const match = /^\s*\[[\s\S]*\]\s*$/m.exec(raw);
if (!match || match.length !== 1) {
throw new Error('Could not parse result of `pnpm list --json`' + raw);
}
const usingPackagedDependencies = Array.isArray(packagedDependencies);
const trees = _.values(JSON.parse(match[0])[0].dependencies) as PnpmTreeNode[];
let result = (
await Promise.all(
trees.map(tree => asPnpmDependency(path.join(cwd, 'node_modules'), tree, !usingPackagedDependencies))
)
).filter(dep => !!dep);
if (usingPackagedDependencies) {
result = selectYarnDependencies(result, packagedDependencies);
}
return result;
}
async function getYarnProductionDependencies(cwd: string, packagedDependencies?: string[]): Promise<YarnDependency[]> { async function getYarnProductionDependencies(cwd: string, packagedDependencies?: string[]): Promise<YarnDependency[]> {
const raw = await new Promise<string>((c, e) => cp.exec('yarn list --prod --json', { cwd, encoding: 'utf8', env: { ...process.env }, maxBuffer: 5000 * 1024 }, (err, stdout) => err ? e(err) : c(stdout))); const raw = await new Promise<string>((c, e) => cp.exec('yarn list --prod --json', { cwd, encoding: 'utf8', env: { ...process.env }, maxBuffer: 5000 * 1024 }, (err, stdout) => err ? e(err) : c(stdout)));
const match = /^{"type":"tree".*$/m.exec(raw); const match = /^{"type":"tree".*$/m.exec(raw);
@ -179,8 +237,34 @@ async function getYarnDependencies(cwd: string, packagedDependencies?: string[])
return _.uniq(result); return _.uniq(result);
} }
export function getDependencies(cwd: string, useYarn = false, packagedDependencies?: string[]): Promise<string[]> { async function getPnpmDependencies(cwd: string, packagedDependencies?: string[]): Promise<string[]> {
return useYarn ? getYarnDependencies(cwd, packagedDependencies) : getNpmDependencies(cwd); const result: string[] = [cwd];
if (await new Promise(c => fs.exists(path.join(cwd, 'pnpm-lock.yaml'), c))) {
const deps = await getPnpmProductionDependencies(cwd, packagedDependencies);
const flatten = (dep: YarnDependency) => { result.push(dep.path); dep.children.forEach(flatten); };
deps.forEach(flatten);
}
return _.uniq(result);
}
export function getDependencies(cwd: string, useYarn = false, usePackageManager: "yarn" | "npm" | "pnpm", packagedDependencies?: string[]): Promise<string[]> {
if(useYarn) {
return getYarnDependencies(cwd, packagedDependencies);
}
else if(usePackageManager == "npm") {
return getNpmDependencies(cwd);
}
else if(usePackageManager == "pnpm") {
return getPnpmDependencies(cwd, packagedDependencies);
}
else if(usePackageManager == "yarn") {
return getYarnDependencies(cwd, packagedDependencies);
}
else {
return getNpmDependencies(cwd);
}
} }
export function getLatestVersion(name: string, cancellationToken?: CancellationToken): Promise<string> { export function getLatestVersion(name: string, cancellationToken?: CancellationToken): Promise<string> {

View file

@ -64,6 +64,7 @@ export interface IPackageOptions {
baseContentUrl?: string; baseContentUrl?: string;
baseImagesUrl?: string; baseImagesUrl?: string;
useYarn?: boolean; useYarn?: boolean;
usePackageManager?: "yarn" | "npm" | "pnpm";
dependencyEntryPoints?: string[]; dependencyEntryPoints?: string[];
ignoreFile?: string; ignoreFile?: string;
expandGitHubIssueLinks?: boolean; expandGitHubIssueLinks?: boolean;
@ -919,8 +920,8 @@ const defaultIgnore = [
'**/.vscode-test/**' '**/.vscode-test/**'
]; ];
function collectAllFiles(cwd: string, useYarn = false, dependencyEntryPoints?: string[]): Promise<string[]> { function collectAllFiles(cwd: string, useYarn = false, usePackageManager: "yarn" | "npm" | "pnpm" = "npm", dependencyEntryPoints?: string[]): Promise<string[]> {
return getDependencies(cwd, useYarn, dependencyEntryPoints).then(deps => { return getDependencies(cwd, useYarn, usePackageManager, dependencyEntryPoints).then(deps => {
const promises: Promise<string[]>[] = deps.map(dep => { const promises: Promise<string[]>[] = deps.map(dep => {
return glob('**', { cwd: dep, nodir: true, dot: true, ignore: 'node_modules/**' }) return glob('**', { cwd: dep, nodir: true, dot: true, ignore: 'node_modules/**' })
.then(files => files .then(files => files
@ -932,8 +933,8 @@ function collectAllFiles(cwd: string, useYarn = false, dependencyEntryPoints?: s
}); });
} }
function collectFiles(cwd: string, useYarn = false, dependencyEntryPoints?: string[], ignoreFile?: string): Promise<string[]> { function collectFiles(cwd: string, useYarn = false, usePackageManager: "yarn" | "npm" | "pnpm" = "npm", dependencyEntryPoints?: string[], ignoreFile?: string): Promise<string[]> {
return collectAllFiles(cwd, useYarn, dependencyEntryPoints).then(files => { return collectAllFiles(cwd, useYarn, usePackageManager, dependencyEntryPoints).then(files => {
files = files.filter(f => !/\r$/m.test(f)); files = files.filter(f => !/\r$/m.test(f));
return readFile(ignoreFile ? ignoreFile : path.join(cwd, '.vscodeignore'), 'utf8') return readFile(ignoreFile ? ignoreFile : path.join(cwd, '.vscodeignore'), 'utf8')
@ -997,11 +998,12 @@ export function createDefaultProcessors(manifest: Manifest, options: IPackageOpt
export function collect(manifest: Manifest, options: IPackageOptions = {}): Promise<IFile[]> { export function collect(manifest: Manifest, options: IPackageOptions = {}): Promise<IFile[]> {
const cwd = options.cwd || process.cwd(); const cwd = options.cwd || process.cwd();
const useYarn = options.useYarn || false; const useYarn = options.useYarn || false;
const usePackageManager = options.usePackageManager || "npm";
const packagedDependencies = options.dependencyEntryPoints || undefined; const packagedDependencies = options.dependencyEntryPoints || undefined;
const ignoreFile = options.ignoreFile || undefined; const ignoreFile = options.ignoreFile || undefined;
const processors = createDefaultProcessors(manifest, options); const processors = createDefaultProcessors(manifest, options);
return collectFiles(cwd, useYarn, packagedDependencies, ignoreFile).then(fileNames => { return collectFiles(cwd, useYarn, usePackageManager, packagedDependencies, ignoreFile).then(fileNames => {
const files = fileNames.map(f => ({ path: `extension/${f}`, localPath: path.join(cwd, f) })); const files = fileNames.map(f => ({ path: `extension/${f}`, localPath: path.join(cwd, f) }));
return processFiles(processors, files); return processFiles(processors, files);
@ -1029,15 +1031,28 @@ function getDefaultPackageName(manifest: Manifest): string {
return `${manifest.name}-${manifest.version}.vsix`; return `${manifest.name}-${manifest.version}.vsix`;
} }
async function prepublish(cwd: string, manifest: Manifest, useYarn: boolean = false): Promise<void> { async function prepublish(cwd: string, manifest: Manifest, useYarn: boolean = false, usePackageManager: "yarn" | "npm" | "pnpm" = "npm"): Promise<void> {
if (!manifest.scripts || !manifest.scripts['vscode:prepublish']) { if (!manifest.scripts || !manifest.scripts['vscode:prepublish']) {
return; return;
} }
console.log(`Executing prepublish script '${useYarn ? 'yarn' : 'npm'} run vscode:prepublish'...`); let tool = 'npm';
if(useYarn) {
tool = 'yarn'
}
else if(usePackageManager == "npm") {
tool = 'npm';
}
else if(usePackageManager == "pnpm") {
tool = 'pnpm';
}
else if(usePackageManager == "yarn") {
tool = 'yarn'
}
console.log(`Executing prepublish script '${tool} run vscode:prepublish'...`);
await new Promise((c, e) => { await new Promise((c, e) => {
const tool = useYarn ? 'yarn' : 'npm';
const child = cp.spawn(tool, ['run', 'vscode:prepublish'], { cwd, shell: true, stdio: 'inherit' }); const child = cp.spawn(tool, ['run', 'vscode:prepublish'], { cwd, shell: true, stdio: 'inherit' });
child.on('exit', code => code === 0 ? c() : e(`${tool} failed with exit code ${code}`)); child.on('exit', code => code === 0 ? c() : e(`${tool} failed with exit code ${code}`));
child.on('error', e); child.on('error', e);
@ -1067,7 +1082,7 @@ export async function pack(options: IPackageOptions = {}): Promise<IPackageResul
const manifest = await readManifest(cwd); const manifest = await readManifest(cwd);
await prepublish(cwd, manifest, options.useYarn); await prepublish(cwd, manifest, options.useYarn, options.usePackageManager);
const files = await collect(manifest, options); const files = await collect(manifest, options);
const jsFiles = files.filter(f => /\.js$/i.test(f.path)); const jsFiles = files.filter(f => /\.js$/i.test(f.path));
@ -1103,17 +1118,17 @@ export async function packageCommand(options: IPackageOptions = {}): Promise<any
/** /**
* Lists the files included in the extension's package. Does not run prepublish. * Lists the files included in the extension's package. Does not run prepublish.
*/ */
export function listFiles(cwd = process.cwd(), useYarn = false, packagedDependencies?: string[], ignoreFile?: string): Promise<string[]> { export function listFiles(cwd = process.cwd(), useYarn = false, usePackageManager: "yarn" | "npm" | "pnpm" = "npm", packagedDependencies?: string[], ignoreFile?: string): Promise<string[]> {
return readManifest(cwd) return readManifest(cwd)
.then(() => collectFiles(cwd, useYarn, packagedDependencies, ignoreFile)); .then(() => collectFiles(cwd, useYarn, usePackageManager, packagedDependencies, ignoreFile));
} }
/** /**
* Lists the files included in the extension's package. Runs prepublish. * Lists the files included in the extension's package. Runs prepublish.
*/ */
export function ls(cwd = process.cwd(), useYarn = false, packagedDependencies?: string[], ignoreFile?: string): Promise<void> { export function ls(cwd = process.cwd(), useYarn = false, usePackageManager?: "yarn" | "npm" | "pnpm", packagedDependencies?: string[], ignoreFile?: string): Promise<void> {
return readManifest(cwd) return readManifest(cwd)
.then(manifest => prepublish(cwd, manifest, useYarn)) .then(manifest => prepublish(cwd, manifest, useYarn, usePackageManager))
.then(() => collectFiles(cwd, useYarn, packagedDependencies, ignoreFile)) .then(() => collectFiles(cwd, useYarn, usePackageManager, packagedDependencies, ignoreFile))
.then(files => files.forEach(f => console.log(`${f}`))); .then(files => files.forEach(f => console.log(`${f}`)));
} }

View file

@ -96,6 +96,7 @@ export interface IPublishOptions {
baseContentUrl?: string; baseContentUrl?: string;
baseImagesUrl?: string; baseImagesUrl?: string;
useYarn?: boolean; useYarn?: boolean;
usePackageManager?: "yarn" | "npm" | "pnpm";
noVerify?: boolean; noVerify?: boolean;
ignoreFile?: string; ignoreFile?: string;
web?: boolean; web?: boolean;