Skip to content

Commit

Permalink
Implements yarn init --install (#618)
Browse files Browse the repository at this point in the history
* Implements yarn init --install

* Commit generated via `yarn stage`

* Fixes

* Fix
  • Loading branch information
arcanis committed Dec 2, 2019
1 parent c6d14ba commit 2d72773
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 27 deletions.
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

0 comments on commit 2d72773

Please sign in to comment.