Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Implements the -u flag
  • Loading branch information
arcanis committed Mar 15, 2022
commit 3f94011717c45370d159637e0fd0b6f66f43d4df
146 changes: 73 additions & 73 deletions .pnp.cjs

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions .yarn/patches/lodash-npm-4.17.21-6382451519.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
diff --git a/package.json b/package.json
index b35fd95cc7aa288e29b3d9b18defc946d610833b..1b05eed0c23c59d907e83839e6247e9a106c347f 100644
--- a/package.json
+++ b/package.json
@@ -6,6 +6,7 @@
"homepage": "https://lodash.com/",
"repository": "lodash/lodash",
"icon": "https://lodash.com/icon.svg",
+ "test": "foo",
"license": "MIT",
"main": "lodash.js",
"author": "John-David Dalton <[email protected]>",
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import {Filename, npath, ppath, xfs} from '@yarnpkg/fslib';

describe(`Commands`, () => {
describe(`patch`, () => {
test(
`it should restart the patch from scratch on subsequent patches by default`,
makeTemporaryEnv({
dependencies: {
[`no-deps`]: `1.0.0`,
},
}, async ({path, run, source}) => {
await run(`install`);

const notFound = {
externalException: {
code: `MODULE_NOT_FOUND`,
},
};

await expect(source(`require('no-deps/foo')`)).rejects.toMatchObject(notFound);
await expect(source(`require('no-deps/bar')`)).rejects.toMatchObject(notFound);

{
const {stdout} = await run(`patch`, `no-deps`, `--json`);
const {path: updateFolderN} = JSON.parse(stdout);

const updateFolder = npath.toPortablePath(updateFolderN);
const updateFile = ppath.join(updateFolder, `foo.js` as Filename);

const fileUser = `module.exports = 'foo';\n`;
await xfs.writeFilePromise(updateFile, fileUser);

await run(`patch-commit`, `-s`, npath.fromPortablePath(updateFolder));
await run(`install`);

await expect(source(`require('no-deps/foo')`)).resolves.toEqual(`foo`);
await expect(source(`require('no-deps/bar')`)).rejects.toMatchObject(notFound);
}

{
const {stdout} = await run(`patch`, `no-deps`, `--json`);
const {path: updateFolderN} = JSON.parse(stdout);

const updateFolder = npath.toPortablePath(updateFolderN);
const updateFile = ppath.join(updateFolder, `bar.js` as Filename);

const fileUser = `module.exports = 'bar';\n`;
await xfs.writeFilePromise(updateFile, fileUser);

await run(`patch-commit`, `-s`, npath.fromPortablePath(updateFolder));
await run(`install`);

await expect(source(`require('no-deps/foo')`)).rejects.toMatchObject(notFound);
await expect(source(`require('no-deps/bar')`)).resolves.toEqual(`bar`);
}
}),
);

test(
`it should augment current patches when using the -u flag`,
makeTemporaryEnv({
dependencies: {
[`no-deps`]: `1.0.0`,
},
}, async ({path, run, source}) => {
await run(`install`);

const notFound = {
externalException: {
code: `MODULE_NOT_FOUND`,
},
};

await expect(source(`require('no-deps/foo')`)).rejects.toMatchObject(notFound);
await expect(source(`require('no-deps/bar')`)).rejects.toMatchObject(notFound);

{
const {stdout} = await run(`patch`, `no-deps`, `--json`);
const {path: updateFolderN} = JSON.parse(stdout);

const updateFolder = npath.toPortablePath(updateFolderN);
const updateFile = ppath.join(updateFolder, `foo.js` as Filename);

const fileUser = `module.exports = 'foo';\n`;
await xfs.writeFilePromise(updateFile, fileUser);

await run(`patch-commit`, `-s`, npath.fromPortablePath(updateFolder));
await run(`install`);

await expect(source(`require('no-deps/foo')`)).resolves.toEqual(`foo`);
await expect(source(`require('no-deps/bar')`)).rejects.toMatchObject(notFound);
}

{
const {stdout} = await run(`patch`, `no-deps`, `-u`, `--json`);
const {path: updateFolderN} = JSON.parse(stdout);

const updateFolder = npath.toPortablePath(updateFolderN);
const updateFile = ppath.join(updateFolder, `bar.js` as Filename);

const fileUser = `module.exports = 'bar';\n`;
await xfs.writeFilePromise(updateFile, fileUser);

await run(`patch-commit`, `-s`, npath.fromPortablePath(updateFolder));
await run(`install`);

await expect(source(`require('no-deps/foo')`)).resolves.toEqual(`foo`);
await expect(source(`require('no-deps/bar')`)).resolves.toEqual(`bar`);
}
}),
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ describe(`Commands`, () => {
const manifest = await xfs.readJsonPromise(ppath.join(path, Filename.manifest));

expect(manifest.dependencies).toEqual({
[`one-fixed-dep`]: `patch:one-fixed-dep@1.0.0#.yarn/patches/one-fixed-dep-npm-1.0.0-b02516a4af.patch`,
[`one-fixed-dep`]: `patch:one-fixed-dep@npm%3A1.0.0#~/.yarn/patches/one-fixed-dep-npm-1.0.0-b02516a4af.patch`,
});

expect(manifest).not.toHaveProperty(`resolutions`);
Expand Down Expand Up @@ -114,7 +114,89 @@ describe(`Commands`, () => {
});

expect(manifest.resolutions).toEqual({
[`[email protected]`]: `patch:[email protected]#.yarn/patches/no-deps-npm-1.0.0-cf533b267a.patch`,
[`[email protected]`]: `patch:no-deps@npm%3A1.0.0#~/.yarn/patches/no-deps-npm-1.0.0-cf533b267a.patch`,
});
}),
);

test(
`it should replace the patch when calling patch-commit again, not wrap it into another patch layer`,
makeTemporaryEnv({
dependencies: {
[`one-fixed-dep`]: `1.0.0`,
},
}, async ({path, run, source}) => {
await run(`install`);

{
const {stdout} = await run(`patch`, `one-fixed-dep`, `--json`);
const {path: updateFolderN} = JSON.parse(stdout);

const updateFolder = npath.toPortablePath(updateFolderN);
const updateFile = ppath.join(updateFolder, `new.js` as Filename);

const fileUser = `module.exports = 42;\n`;
await xfs.writeFilePromise(updateFile, fileUser);

await run(`patch-commit`, `-s`, npath.fromPortablePath(updateFolder));

const manifest = await xfs.readJsonPromise(ppath.join(path, Filename.manifest));

expect(manifest.dependencies).toEqual({
[`one-fixed-dep`]: `patch:one-fixed-dep@npm%3A1.0.0#~/.yarn/patches/one-fixed-dep-npm-1.0.0-b02516a4af.patch`,
});
}

{
const {stdout} = await run(`patch`, `one-fixed-dep`, `--json`);
const {path: updateFolderN} = JSON.parse(stdout);

const updateFolder = npath.toPortablePath(updateFolderN);
const updateFile = ppath.join(updateFolder, `new.js` as Filename);

const fileUser = `module.exports = 21;\n`;
await xfs.writeFilePromise(updateFile, fileUser);

await run(`patch-commit`, `-s`, npath.fromPortablePath(updateFolder));

const manifest = await xfs.readJsonPromise(ppath.join(path, Filename.manifest));

expect(manifest.dependencies).toEqual({
[`one-fixed-dep`]: `patch:one-fixed-dep@npm%3A1.0.0#~/.yarn/patches/one-fixed-dep-npm-1.0.0-b02516a4af.patch`,
});
}
}),
);

test(
`it should be able to patch virtual packages`,
makeTemporaryEnv({
dependencies: {
[`peer-deps-lvl0`]: `1.0.0`,
},
}, async ({path, run, source}) => {
await run(`install`);

const {stdout} = await run(`patch`, `peer-deps-lvl1`, `--json`);
const {path: updateFolderN} = JSON.parse(stdout);

const updateFolder = npath.toPortablePath(updateFolderN);
const updateFile = ppath.join(updateFolder, `index.js` as Filename);

const fileSource = await xfs.readFilePromise(updateFile, `utf8`);
const fileUser = fileSource.replace(`require(dep)`, `42`);
await xfs.writeFilePromise(updateFile, fileUser);

await run(`patch-commit`, `-s`, npath.fromPortablePath(updateFolder));

const manifest = await xfs.readJsonPromise(ppath.join(path, Filename.manifest));

expect(manifest.dependencies).toEqual({
[`peer-deps-lvl0`]: `1.0.0`,
});

expect(manifest.resolutions).toEqual({
[`[email protected]`]: `patch:peer-deps-lvl1@npm%3A1.0.0#~/.yarn/patches/peer-deps-lvl1-npm-1.0.0-894d37389e.patch`,
});
}),
);
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-compat/sources/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const plugin: Plugin<CoreHooks & PatchHooks> = {
return structUtils.makeDescriptor(dependency, structUtils.makeRange({
protocol: `patch:`,
source: structUtils.stringifyDescriptor(dependency),
selector: `~builtin<compat/${structUtils.stringifyIdent(dependency)}>`,
selector: `optional!builtin<compat/${structUtils.stringifyIdent(dependency)}>`,
params: null,
}));
},
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-patch/sources/PatchFetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {reportHunk}

export class PatchFetcher implements Fetcher {
supports(locator: Locator, opts: MinimalFetchOptions) {
if (!locator.reference.startsWith(`patch:`))
if (!patchUtils.isPatchLocator(locator))
return false;

return true;
Expand Down
4 changes: 2 additions & 2 deletions packages/plugin-patch/sources/PatchResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ const CACHE_VERSION = 3;

export class PatchResolver implements Resolver {
supportsDescriptor(descriptor: Descriptor, opts: MinimalResolveOptions) {
if (!descriptor.range.startsWith(`patch:`))
if (!patchUtils.isPatchDescriptor(descriptor))
return false;

return true;
}

supportsLocator(locator: Locator, opts: MinimalResolveOptions) {
if (!locator.reference.startsWith(`patch:`))
if (!patchUtils.isPatchLocator(locator))
return false;

return true;
Expand Down
30 changes: 22 additions & 8 deletions packages/plugin-patch/sources/commands/patch.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {BaseCommand, WorkspaceRequiredError} from '@yarnpkg/cli';
import {Cache, Configuration, Project, formatUtils, structUtils, StreamReport, MessageName, miscUtils} from '@yarnpkg/core';
import {npath} from '@yarnpkg/fslib';
import {Command, Option, Usage, UsageError} from 'clipanion';
import {BaseCommand, WorkspaceRequiredError} from '@yarnpkg/cli';
import {Cache, Configuration, Project, formatUtils, structUtils, StreamReport, MessageName, miscUtils, Manifest} from '@yarnpkg/core';
import {npath} from '@yarnpkg/fslib';
import {Command, Option, Usage, UsageError} from 'clipanion';

import * as patchUtils from '../patchUtils';
import * as patchUtils from '../patchUtils';

// eslint-disable-next-line arca/no-default-export
export default class PatchCommand extends BaseCommand {
Expand All @@ -15,11 +15,17 @@ export default class PatchCommand extends BaseCommand {
description: `prepare a package for patching`,
details: `
This command will cause a package to be extracted in a temporary directory intended to be editable at will.

Once you're done with your changes, run \`yarn patch-commit -s <path>\` (with \`<path>\` being the temporary directory you received) to generate a patchfile and register it into your top-level manifest via the \`patch:\` protocol. Run \`yarn patch-commit -h\` for more details.

Calling the command when you already have a patch won't import it by default (in other words, the default behavior is to reset existing patches). However, adding the \`-u,--update\` flag will import any current patch.
`,
});

update = Option.Boolean(`-u,--update`, false, {
description: `Reapply local patches that already apply to this packages`,
});

json = Option.Boolean(`--json`, false, {
description: `Format the output as an NDJSON stream`,
});
Expand All @@ -46,6 +52,9 @@ export default class PatchCommand extends BaseCommand {
if (structUtils.isVirtualLocator(pkg))
return miscUtils.mapAndFilter.skip;

if (patchUtils.isPatchLocator(pkg) !== this.update)
return miscUtils.mapAndFilter.skip;

return pkg;
});

Expand All @@ -65,14 +74,19 @@ export default class PatchCommand extends BaseCommand {
json: this.json,
stdout: this.context.stdout,
}, async report => {
const unpatchedLocator = patchUtils.ensureUnpatchedLocator(locator);
const temp = await patchUtils.extractPackageToDisk(locator, {cache, project});

report.reportJson({
locator: structUtils.stringifyLocator(locator),
locator: structUtils.stringifyLocator(unpatchedLocator),
path: npath.fromPortablePath(temp),
});

report.reportInfo(MessageName.UNNAMED, `Package ${structUtils.prettyLocator(configuration, locator)} got extracted with success!`);
const updateString = this.update
? ` along with its current modifications`
: ``;

report.reportInfo(MessageName.UNNAMED, `Package ${structUtils.prettyLocator(configuration, unpatchedLocator)} got extracted with success${updateString}!`);
report.reportInfo(MessageName.UNNAMED, `You can now edit the following folder: ${formatUtils.pretty(configuration, npath.fromPortablePath(temp), `magenta`)}`);
report.reportInfo(MessageName.UNNAMED, `Once you are done run ${formatUtils.pretty(configuration, `yarn patch-commit -s ${process.platform === `win32` ? `"` : ``}${npath.fromPortablePath(temp)}${process.platform === `win32` ? `"` : ``}`, `cyan`)} and Yarn will store a patchfile based on your changes.`);
});
Expand Down
43 changes: 22 additions & 21 deletions packages/plugin-patch/sources/commands/patchCommit.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {BaseCommand, WorkspaceRequiredError} from '@yarnpkg/cli';
import {Configuration, Descriptor, DescriptorHash, Project, structUtils, Workspace} from '@yarnpkg/core';
import {npath, xfs, ppath, PortablePath, Filename} from '@yarnpkg/fslib';
import {Command, Option, Usage, UsageError} from 'clipanion';
import {BaseCommand, WorkspaceRequiredError} from '@yarnpkg/cli';
import {Configuration, Descriptor, DescriptorHash, Manifest, Project, structUtils, Workspace} from '@yarnpkg/core';
import {npath, xfs, ppath, PortablePath, Filename} from '@yarnpkg/fslib';
import {Command, Option, Usage, UsageError} from 'clipanion';

import * as patchUtils from '../patchUtils';
import * as patchUtils from '../patchUtils';

// eslint-disable-next-line arca/no-default-export
export default class PatchCommitCommand extends BaseCommand {
Expand Down Expand Up @@ -75,10 +75,9 @@ export default class PatchCommitCommand extends BaseCommand {
continue;

const devirtualizedDescriptor = structUtils.ensureDevirtualizedDescriptor(descriptor);
if (!devirtualizedDescriptor)
continue;
const unpatchedDescriptor = patchUtils.ensureUnpatchedDescriptor(devirtualizedDescriptor);

const resolution = project.storedResolutions.get(devirtualizedDescriptor.descriptorHash);
const resolution = project.storedResolutions.get(unpatchedDescriptor.descriptorHash);
if (!resolution)
throw new Error(`Assertion failed: Expected the resolution to have been registered`);

Expand All @@ -103,24 +102,26 @@ export default class PatchCommitCommand extends BaseCommand {
}

for (const workspace of workspaceDependents) {
const originalDescriptor = workspace.manifest.dependencies.get(locator.identHash);
if (!originalDescriptor)
throw new Error(`Assertion failed: Expected the package to have been registered`);

const newDescriptor = patchUtils.makeDescriptor(originalDescriptor, {
parentLocator: null,
sourceDescriptor: originalDescriptor,
patchPaths: [ppath.relative(workspace.cwd, patchPath)],
});

workspace.manifest.dependencies.set(originalDescriptor.identHash, newDescriptor);
for (const dependencyType of Manifest.hardDependencies) {
const originalDescriptor = workspace.manifest[dependencyType].get(locator.identHash);
if (!originalDescriptor)
continue;

const newDescriptor = patchUtils.makeDescriptor(originalDescriptor, {
parentLocator: null,
sourceDescriptor: structUtils.convertLocatorToDescriptor(locator),
patchPaths: [ppath.join(Filename.home, ppath.relative(project.cwd, patchPath))],
});

workspace.manifest[dependencyType].set(originalDescriptor.identHash, newDescriptor);
}
}

for (const originalDescriptor of transitiveDependencies.values()) {
const newDescriptor = patchUtils.makeDescriptor(originalDescriptor, {
parentLocator: null,
sourceDescriptor: originalDescriptor,
patchPaths: [ppath.relative(workspace.cwd, patchPath)],
sourceDescriptor: structUtils.convertLocatorToDescriptor(locator),
patchPaths: [ppath.join(Filename.home, ppath.relative(project.cwd, patchPath))],
});

project.topLevelWorkspace.manifest.resolutions.push({
Expand Down
Loading