Skip to content
This repository has been archived by the owner on Oct 1, 2024. It is now read-only.

Add directory key to package.json repository #1233

Merged
merged 7 commits into from
Jan 27, 2020

Conversation

sambostock
Copy link
Contributor

Description

TL;DR

#1224 should have made this change instead:

 "name": "@shopify/{{name}}",
 "repository": {
   "type": "git",
   "url": "git+https://github.com/Shopify/quilt.git",
+  "directory": "packages/{{name}}",
 },

Details

This follows up on #1224, which updated the repository fields, in an effort to improve support for tools such as Dependabot to deep-link into the repo:

 "name": "@shopify/{{name}}",
-"repository": {
-  "type": "git",
-  "url": "git+https://github.com/Shopify/quilt.git"
-},
+"repository": "https://github.com/shopify/quilt/tree/master/packages/{{name}}",

While these links do work, they still did not improve integration with Dependabot.

After speaking to Dependabot support, and being referred to React's package.json repository entry, it looks like specifying the additional directory key is the answer:

 "name": "@shopify/{{name}}",
-"repository": "https://github.com/shopify/quilt/tree/master/packages/{{name}}",
+"repository": {
+  "type": "git",
+  "url": "git+https://github.com/Shopify/quilt.git",
+  "directory": "packages/{{name}}",
+},

This makes that change, fixes some other small inconsistencies, and adds tests along the way.

Type of change

  • Patch: Bug/ Documentation fix (non-breaking change which fixes an issue or adds documentation)

Checklist

  • I have added a changelog entry, prefixed by the type of change noted above

Copy link
Contributor Author

@sambostock sambostock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a few outstanding discrepancies causing the tests for some packages to fail. I'm looking for feedback from reviewers on how to address them.

Once they've been addressed, I'll update the tests, remove the FIXME comments, and address any other linting errors.

test/consistent-package-json.test.ts Outdated Show resolved Hide resolved
});

it('specifies the expected main', () => {
if(packageName === 'graphql-persisted') return; // FIXME: Address this in graphql-persisted
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The graphql-persisted package does not specify an entry for main.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yah, a main doesn't really make sense to me here. The package has two equally-important consumers (koa, apollo), so there isn't a good default import source.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it not still make sense to have a main index.js that consists of something like

export {apollo} from './apollo';
export {apollo} from './koa';

This would make it conform to the expected main entry point, while still offering the option to import the desired consumer directly. As a side benefit, it would also allow the types entry to be consistent.

test/consistent-package-json.test.ts Outdated Show resolved Hide resolved
});

it('specifies the expected types', () => {
if(packageName === 'graphql-persisted') return; // FIXME
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The graphql-persisted package fails to specify types.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the main conversation for this package, this seems fine to me. Consumers will directly import:

@sambostock sambostock changed the title Standardize package dot json Add directory key to package.json repository Jan 12, 2020
Copy link
Member

@GoodForOneFare GoodForOneFare left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 A lot of bikeshedding from me about removing some cognitive load from test expectations, but everything looks sound to me. Thank you 💟

test/consistent-package-json.test.ts Outdated Show resolved Hide resolved
});

it('specifies the expected main', () => {
if(packageName === 'graphql-persisted') return; // FIXME: Address this in graphql-persisted
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yah, a main doesn't really make sense to me here. The package has two equally-important consumers (koa, apollo), so there isn't a good default import source.

test/consistent-package-json.test.ts Outdated Show resolved Hide resolved
});

it('specifies the expected types', () => {
if(packageName === 'graphql-persisted') return; // FIXME
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the main conversation for this package, this seems fine to me. Consumers will directly import:

packages/web-worker/package.json Show resolved Hide resolved
test/consistent-package-json.test.ts Outdated Show resolved Hide resolved
test/consistent-package-json.test.ts Outdated Show resolved Hide resolved
});

it('specifies the MIT license', () => {
expect(packageJSON.license).toBe(packageJSONTemplate.license);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
expect(packageJSON.license).toBe(packageJSONTemplate.license);
expect(packageJSON.license).toBe('MIT');

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had originally hardcoded things like this in this file, but then I started adding tests for the template as well. I realized that it made more sense to treat the template as the expected values, and ensure each package conforms to it.

If we hard code MIT here, it means that if we change the template down the road, the change will only be detected by the test suite after generating a new package and seeing it fail the suite. By coupling to the template, changing the license there would cause every package's license test to fail, which is likely what we want, since we'd probably want to update their licenses as well.

});

it('specifies Quilt deep-link homepage', () => {
const expectedHomepage = compile(packageJSONTemplate.homepage);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it's guaranteeing less, but I'd be happy enough with:

expect(packageJSON.homepage).toEndWith(`/${basename(packageJSONTemplatePath)}/README.md`)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what loosening this would gain us. I think the compile version is clearer, since it matches what is used in the other tests, and couples to the template as authoritative.

it('specifies a repository deep-linking into the Quilt monorepo', () => {
const expectedRepository = {
...packageJSONTemplate.repository,
directory: compile(packageJSONTemplate.repository.directory),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, simplifying this to check for packages/${basename(packageJsonPath)} seems fine.

Copy link
Contributor Author

@sambostock sambostock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review @GoodForOneFare! I've responded to a number of the comments.

Something that came up in a couple comments was the use of the compile helper, and the suggestion to decouple from the template. I feel that the template code and package specifications should match. Updating the template should probably require updating the packages to match. Decoupling from the template means packages can diverge, leading to inconsistencies, even if small.

That said, I agree that in some cases the use of compile is a little confusing. I considered deep-compiling the entire template for each package. This would allow usage like

expect(packageJSON.foo).toBe(expectedPackageJSON.foo);

I opted not to, as it would mean having to deeply compile the entire template for each package. If that's clearer though, I could switch it over to that.

It does occur to me that I could replace {{name}} in the raw JSON before parsing it, which would be simpler than deeply replacing {{name}} in the parsed object.

test/consistent-package-json.test.ts Outdated Show resolved Hide resolved
test/consistent-package-json.test.ts Show resolved Hide resolved
test/consistent-package-json.test.ts Outdated Show resolved Hide resolved
});

it('specifies Quilt deep-link homepage', () => {
const expectedHomepage = compile(packageJSONTemplate.homepage);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what loosening this would gain us. I think the compile version is clearer, since it matches what is used in the other tests, and couples to the template as authoritative.

});

it('specifies the MIT license', () => {
expect(packageJSON.license).toBe(packageJSONTemplate.license);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had originally hardcoded things like this in this file, but then I started adding tests for the template as well. I realized that it made more sense to treat the template as the expected values, and ensure each package conforms to it.

If we hard code MIT here, it means that if we change the template down the road, the change will only be detected by the test suite after generating a new package and seeing it fail the suite. By coupling to the template, changing the license there would cause every package's license test to fail, which is likely what we want, since we'd probably want to update their licenses as well.

});

it('specifies the expected main', () => {
if(packageName === 'graphql-persisted') return; // FIXME: Address this in graphql-persisted
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it not still make sense to have a main index.js that consists of something like

export {apollo} from './apollo';
export {apollo} from './koa';

This would make it conform to the expected main entry point, while still offering the option to import the desired consumer directly. As a side benefit, it would also allow the types entry to be consistent.

test/consistent-package-json.test.ts Outdated Show resolved Hide resolved
expect(packageJSON.types).toBe(packageJSONTemplate.types);
});

it('specifies a version', () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair. Should it be this file's responsibility to catch it before it gets to the publish stage though?

@sambostock
Copy link
Contributor Author

@GoodForOneFare, I've added a commit in which I switch to pre-compiling the expected template for each package, instead of using compile on a per-value basis. It does look much cleaner, while keeping things based on the template, though at the cost of generating the expected contents each time (probably fine).

@sambostock
Copy link
Contributor Author

I've addressed all exceptional packages and lint errors:

  • polyfills now specifies files: ["dist/*", "!tsconfig.tsbuildinfo"]
  • csrf-token-fetcher, dates, react-effect, react-google-analytics, and useful-types have had sideEffects: false added
  • graphql-persisted now exposes a main entrypoint which re-exports the existing entrypoints
    import * as apollo from './apollo';
    import * as koaMiddleware from './koa-middleware';
    
    export {apollo, koaMiddleware};

@GoodForOneFare, should these changes have accompanying changelog entries? Should they be in a separate PR, or are they fine here?

packages/graphql-persisted/package.json Outdated Show resolved Hide resolved
packages/graphql-persisted/src/index.ts Outdated Show resolved Hide resolved
test/consistent-package-json.test.ts Outdated Show resolved Hide resolved
expect(packageJSON.types).toBe(packageJSONTemplate.types);
});

it('specifies a version', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a moot point because IMO this test will never catch anything 🤷🏻‍♂️ If someone doesn't use the generator:

  • Other 💩 will fail (like the sideEffects check) and they'll end up using the generator / copy+pasting from elsewhere
  • Worst case, they'll use yarn init, which also includes a version

I don't want to delay shipping the PR because of this, but I'd strongly prefer that tests for the sake of completion don't make it into the codebase.

test/consistent-package-json.test.ts Outdated Show resolved Hide resolved
test/consistent-package-json.test.ts Outdated Show resolved Hide resolved
test/consistent-package-json.test.ts Show resolved Hide resolved
@GoodForOneFare
Copy link
Member

RE changelog entries, I guess they have to be part of this PR. Otherwise, any merge gap between this and a changelog PR will result in someone else having to release everything.

@sambostock sambostock force-pushed the standardize-package-dot-json branch 3 times, most recently from eb24a36 to b85e704 Compare January 24, 2020 21:48
@sambostock
Copy link
Contributor Author

@GoodForOneFare, I've done the following:

  • Restored graphql-persisted to what it was, so it is now an exception to the main and types tests.
  • Added selected changelog entries:
    • For the additions of "sideEffects": false across a couple packages
    • For the change to published "files" in polyfills
  • Omitted changelog entries for "Shopify" capitalization, and the directory key, since I don't think consumers need to care.
  • Rebased into meaningful commits, so the diffs are trivial to inspect.

While I had originally agreed with your point on consolidating the tests into one test per key for all packages, I have switched it back to my original one test per key per package. I feel strongly that this is a better developer experience. You brought up two main points:

  • The end result is less noisy during successful runs

    I did some testing, and as far as I can tell, the giant output only shows up if running this single test file, all on its own. When running a typical run, this file shows up just like any other suite (file name only):

    ...
    PASS packages/react-i18n/src/test/e2e/server.test.tsx
    PASS test/consistent-package-json.test.ts                <<<<
    PASS packages/i18n/src/test/pseudotranslate.test.ts
    ...
    

    I noticed, when looking at the CI output, the console.log, console.warn, and console.error output that makes it into the test output provides far more noise than this does. 😅

  • It's rare to touch >3 package.jsons in a PR, so the set of packages to sift through is small

    This applies to PRs that change a package's package.json, but changes to the template, or which add a new field benefit greatly from knowing which package(s) failed. The consolidated output is effectively useless, since it just says there has been a failure and provides no info about where or how many occurrences there are. During my rebase, I had to redo some of my changes, and knowing where I needed to add sideEffects meant I didn't have to sift through 70+ package.json files.

Based on that, I think the benefits of granular failures far outweigh the cost.

Copy link
Member

@GoodForOneFare GoodForOneFare left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 You're a champ, @sambostock! Very much necessary, and very much appreciated 🙏

@sambostock sambostock merged commit bb88ea2 into master Jan 27, 2020
@sambostock sambostock deleted the standardize-package-dot-json branch January 27, 2020 18:46
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants