Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implements yarn init --install #618

Merged
merged 5 commits into from Dec 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/plugin-dlx/sources/commands/dlx.ts
@@ -1,6 +1,7 @@
import {BaseCommand, WorkspaceRequiredError} from '@yarnpkg/cli';
import {Configuration, Project, ThrowReport} from '@yarnpkg/core';
import {scriptUtils, structUtils} from '@yarnpkg/core';
import {DEFAULT_LOCK_FILENAME, DEFAULT_RC_FILENAME} from '@yarnpkg/core';
import {Filename, PortablePath, npath, ppath, toFilename, xfs} from '@yarnpkg/fslib';
import {Command} from 'clipanion';
import tmp from 'tmp';
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-init/package.json
Expand Up @@ -3,7 +3,7 @@
"version": "2.0.0-rc.7",
"nextVersion": {
"semver": "2.0.0-rc.8",
"nonce": "279316411436635"
"nonce": "4149448387893945"
},
"main": "./sources/index.ts",
"dependencies": {
Expand Down
79 changes: 70 additions & 9 deletions packages/plugin-init/sources/commands/init.ts
@@ -1,9 +1,10 @@
import {BaseCommand} from '@yarnpkg/cli';
import {Configuration, Manifest} from '@yarnpkg/core';
import {structUtils} from '@yarnpkg/core';
import {xfs, ppath, toFilename} from '@yarnpkg/fslib';
import {updateAndSave} from '@yarnpkg/json-proxy';
import {Command, UsageError} from 'clipanion';
import {BaseCommand} from '@yarnpkg/cli';
import {Configuration, Manifest} from '@yarnpkg/core';
import {execUtils, scriptUtils, structUtils} from '@yarnpkg/core';
import {xfs, ppath, toFilename, Filename} from '@yarnpkg/fslib';
import {updateAndSave} from '@yarnpkg/json-proxy';
import {Command, UsageError} from 'clipanion';
import {inspect} from 'util';

// eslint-disable-next-line arca/no-default-export
export default class InitCommand extends BaseCommand {
Expand All @@ -13,13 +14,18 @@ export default class InitCommand extends BaseCommand {
@Command.Boolean(`-p,--private`)
private: boolean = false;

@Command.String(`-i,--install`)
install?: string;

static usage = Command.Usage({
description: `create a new package`,
details: `
This command will setup a new package in your local directory.

If the \`-p,--private\` option is set, the package will be private by default.

If the \`-i,--install\` option is given a value, Yarn will first download it using \`yarn set version\` and only then forward the init call to the newly downloaded bundle.

The following settings can be used in order to affect what the generated package.json will look like:

- \`initLicense\`
Expand All @@ -32,27 +38,82 @@ export default class InitCommand extends BaseCommand {
], [
`Create a new private package in the local directory`,
`yarn init -p`,
], [
`Create a new package and store the Yarn release inside`,
`yarn init -i berry`,
]],
});

@Command.Path(`init`)
async execute() {
if (xfs.existsSync(ppath.join(this.context.cwd, toFilename(`package.json`))))
if (xfs.existsSync(ppath.join(this.context.cwd, Manifest.fileName)))
throw new UsageError(`A package.json already exists in the specified directory`);

const configuration = await Configuration.find(this.context.cwd, this.context.plugins);

if (typeof this.install !== `undefined`) {
return await this.executeProxy(configuration);
} else {
return await this.executeRegular(configuration);
}
}

async executeProxy(configuration: Configuration) {
if (configuration.get(`yarnPath`) !== null)
throw new UsageError(`Cannot use the --install flag when the current directory already uses yarnPath (from ${configuration.sources.get(`yarnPath`)})`);

if (configuration.projectCwd !== null)
throw new UsageError(`Cannot use the --install flag when the current directory is already part of a project`);

if (!xfs.existsSync(this.context.cwd))
await xfs.mkdirpPromise(this.context.cwd);

const configuration = await Configuration.find(this.context.cwd, this.context.plugins);
const lockfilePath = ppath.join(this.context.cwd, configuration.get<Filename>(`lockfileFilename`));
if (!xfs.existsSync(lockfilePath))
await xfs.writeFilePromise(lockfilePath, ``);

const versionExitCode = await this.cli.run([`set`, `version`, this.install!]);
if (versionExitCode !== 0)
return versionExitCode;

this.context.stdout.write(`\n`);

const args: Array<string> = [];
if (this.private)
args.push(`-p`);
if (this.yes)
args.push(`-y`);

const {code} = await execUtils.pipevp(`yarn`, [`init`, ...args], {
cwd: this.context.cwd,
stdin: this.context.stdin,
stdout: this.context.stdout,
stderr: this.context.stderr,
env: await scriptUtils.makeScriptEnv(),
});

return code;
}

async executeRegular(configuration: Configuration) {
if (!xfs.existsSync(this.context.cwd))
await xfs.mkdirpPromise(this.context.cwd);

const manifest = new Manifest();
manifest.name = structUtils.makeIdent(configuration.get(`initScope`), ppath.basename(this.context.cwd));
manifest.version = configuration.get(`initVersion`);
manifest.private = this.private;
manifest.license = configuration.get(`initLicense`);

await updateAndSave(ppath.join(this.context.cwd, toFilename(`package.json`)), (tracker: Object) => {
await updateAndSave(ppath.join(this.context.cwd, Manifest.fileName), (tracker: Object) => {
manifest.exportTo(tracker);
});

const inspectable: any = {};
manifest.exportTo(inspectable);

this.context.stdout.write(`${inspect(inspectable, {
depth: Infinity,
})}\n`);
}
}
2 changes: 1 addition & 1 deletion packages/yarnpkg-cli/package.json
Expand Up @@ -3,7 +3,7 @@
"version": "2.0.0-rc.18",
"nextVersion": {
"semver": "2.0.0-rc.19",
"nonce": "8682259403650061"
"nonce": "6173145748735661"
},
"main": "./sources/index.ts",
"dependencies": {
Expand Down
16 changes: 10 additions & 6 deletions packages/yarnpkg-core/sources/Configuration.ts
Expand Up @@ -706,12 +706,16 @@ export class Configuration {
if (xfs.existsSync(ppath.join(currentCwd, toFilename(`package.json`))))
projectCwd = currentCwd;

const topLevelFound = lockfileFilename !== null
? xfs.existsSync(ppath.join(currentCwd, lockfileFilename))
: projectCwd !== null;

if (topLevelFound)
break;
if (lockfileFilename !== null) {
if (xfs.existsSync(ppath.join(currentCwd, lockfileFilename))) {
projectCwd = currentCwd;
break;
}
} else {
if (projectCwd !== null) {
break;
}
}

nextCwd = ppath.dirname(currentCwd);
}
Expand Down
14 changes: 6 additions & 8 deletions packages/yarnpkg-core/sources/Project.ts
Expand Up @@ -82,24 +82,22 @@ export class Project {
if (!configuration.projectCwd)
throw new Error(`No project found in the initial directory`);

let packageCwd = null;
let packageCwd = configuration.projectCwd;

let nextCwd = startingCwd;
let currentCwd = null;

while (currentCwd !== configuration.projectCwd) {
currentCwd = nextCwd;

if (xfs.existsSync(ppath.join(currentCwd, toFilename(`package.json`))))
if (!packageCwd)
packageCwd = currentCwd;
if (xfs.existsSync(ppath.join(currentCwd, toFilename(`package.json`)))) {
packageCwd = currentCwd;
break;
}

nextCwd = ppath.dirname(currentCwd);
}

if (!packageCwd)
throw new Error(`Assertion failed: No manifest found in the project`);

const project = new Project(configuration.projectCwd, {configuration});

await project.setupResolutions();
Expand Down Expand Up @@ -200,7 +198,7 @@ export class Project {
}
}

private async setupWorkspaces({force = false}: {force?: boolean} = {}) {
private async setupWorkspaces() {
this.workspaces = [];

this.workspacesByCwd = new Map();
Expand Down
4 changes: 3 additions & 1 deletion packages/yarnpkg-core/sources/Workspace.ts
Expand Up @@ -42,7 +42,9 @@ export class Workspace {

async setup() {
// @ts-ignore: It's ok to initialize it now
this.manifest = await Manifest.find(this.cwd);
this.manifest = xfs.existsSync(ppath.join(this.cwd, Manifest.fileName))
? await Manifest.find(this.cwd)
: new Manifest();

// We use ppath.relative to guarantee that the default hash will be consistent even if the project is installed on different OS / path
// @ts-ignore: It's ok to initialize it now, even if it's readonly (setup is called right after construction)
Expand Down
2 changes: 1 addition & 1 deletion packages/yarnpkg-core/sources/index.ts
Expand Up @@ -8,6 +8,7 @@ import * as structUtils from './structUtils';
import * as tgzUtils from './tgzUtils';

export {Cache} from './Cache';
export {DEFAULT_RC_FILENAME, DEFAULT_LOCK_FILENAME} from './Configuration';
export {Configuration, FormatType, PluginConfiguration, ProjectLookup, SettingsDefinition, SettingsType} from './Configuration';
export {Fetcher, FetchOptions, FetchResult, MinimalFetchOptions} from './Fetcher';
export {Installer, BuildDirective, BuildType} from './Installer';
Expand All @@ -28,7 +29,6 @@ export {YarnVersion}
export {IdentHash, DescriptorHash, LocatorHash} from './types';
export {Ident, Descriptor, Locator, Package} from './types';
export {LinkType} from './types';

export {hashUtils};
export {httpUtils};
export {execUtils};
Expand Down