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

Introduced new developer experience efficiencies #2702

Draft
wants to merge 14 commits into
base: master
Choose a base branch
from

Conversation

smallsaucepan
Copy link
Member

Reasonably major attempt to streamline the Turfjs development environment for contributors. Overall concept is to avoid generating Javascript build artefacts unless we absolutely have to.

Summary of changes. Some may be contentious, so happy to debate or justify in more depth:

  1. Uses paths config (tsconfig.shared.json) to help build and test tools / editors resolve TS dependencies between packages more directly. Used to have to build dependent packages to JS first and load those
  2. Removed need to build project before testing. Major time saver for Turf org developers
  3. Adopted newer npm lifecycle targets to avoid building things before we need them. Doesn't automatically generate two versions of JS on npm install for example
  4. Added static type checking for all projects. TS packages type check index.ts directly, JS packages test a types.ts scaffold file as the entry point. Detects errors introduced in locally edited dependent packages
  5. Removed most existing types.ts files. Approach above hits the index.ts file directly, and most types.ts file weren't doing much more than serving as an entry point for tsc
  6. Rejigged rollup + last-tests targets in @turf/turf as cdnBundle. Felt this more accurately describes what that's doing
  7. ESM and CJS Javascript, and minified CDN bundle, aren't generated until we are actually preparing to publish a release
  8. Updated module and moduleResolution to esnext and bundler respectively (tsconfig.shared.json). This didn't seem to affect the generated JS output, as the target remains at es2017
  9. At end of CI run all checked in code will have been unit tested and type checked. Artefacts generated for a release (EJS, CJS, etc) will no longer be tested every CI run. However, those are the products of automatic processes so testing every time would rarely uncover problems not already seen earlier in unit tests.

Further background discussion #2629 and some relevant comments on now superceded PR #2682.

…ckage, rather than specifying the arguments on the command line.
…o building to work. Simplifies things overall.
…lined in dist/esm/index.js files, greatly increasing their size.
…re causing problems in a previous build because skipNodeModulesBundle was true. That's now been removed and @turf/turf does build fine without them.
…ome degree (except @turf/turf). TS projects we statically type check the index.ts itself. JS projects we type check a minimal types.ts file against the index.d.ts definition.
…ipt infers type dependencies of imported types. These must have surfaced due to this PRs new approach to linking package dependencies. Solution was to explicitly define a few return types that previously were being inferred. Should be fixed permanently in typescript 5.5, though nothing really for us to revert. See microsoft/TypeScript#47663 (comment)

Also synced some JSDoc with the function's actual type definition.
…ve. Adding empty marchingsquares d.ts to isolines (same as we've done for isobands) to retire a ts-expect-error. Also believe prepublishOnly better target to use than prepublish.
@@ -50,6 +46,56 @@ const JS_TAPE_PACKAGES = TAPE_PACKAGES.filter(

export default {
rules: [
fileContents({
Copy link
Collaborator

Choose a reason for hiding this comment

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

https://monorepolint.com/docs/rules/standard-tsconfig/ maybe we should use standardTsconfig now instead of fileContents. My biggest issue with the current monorepolint is that we've got hand-managed lists of files. Although most of our packages only use a single file, its definitely unexpected if I add another file and the import doesn't work until it gets added in here (and further extends the proliferation of special cases in this file.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I wonder if we can get away with something like this and be a little more forgiving of adding extra files.

{
  "include": ["index.js", "index.ts", "lib/*", "*.d.ts"],
  "exclude": ["index.d.ts"]
}

Copy link
Member Author

Choose a reason for hiding this comment

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

Will check this idea out. Thanks 👍

[0, 0],
[10, 10],
]);
booleanTouches(pt, line);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think the point of these was to help guard against API breaks in the types? Kind of like what DefinitelyTyped does. I'm not sure that that's a super compelling argument here with a relatively basic amount of syntax expressed in this file. The last-checks step is grabbing the code in the examples and checking that the example compiles. Thoughts? (I do kind of want each package to test its own example, documentation generation, etc).

Copy link
Member Author

Choose a reason for hiding this comment

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

Do you mean like a form of API locking between major versions? I thought so too at first. However, I could easily narrow the signature of booleanTouches to Feature, Feature and this test would still pass. As is, I don't think they really protect us much.

I do kind of want each package to test its own example, documentation generation, etc).

Definitely. That is still the case. The tsc --noEmit in each package is checking to see if the module compiles. We don't need to actually call our method to have tsc examine it though. Just importing seems to be enough.

We can add a bunch of example invocations if we want. All we're doing though is proving we know how to call our own library. It's not testing the type safety of the implementation any better.

@@ -2,7 +2,7 @@ import { GeoJsonProperties, FeatureCollection, Point } from "geojson";
import { clone } from "@turf/clone";
import { distance } from "@turf/distance";
import { degreesToRadians, lengthToDegrees, Units } from "@turf/helpers";
import { rbush as RBush } from "./lib/rbush-export.js";
import rbush from "rbush";
Copy link
Collaborator

Choose a reason for hiding this comment

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

Its nice that we can get rid of the import hacks, I'm still trying to fix the underlying typings still.

// Custom Properties
clustered.features[0].properties.cluster;
clustered.features[0].properties.dbscan;
clustered.features[0].properties.foo;
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is an example of a type test that might be more likely to catch typings breaks than just the example for the package.

Copy link
Member Author

Choose a reason for hiding this comment

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

This type checks fine as is. I could make that features[999] though and tsc would still say it's correctly typed. That would error at runtime, so maybe it belongs more in test.ts?

A test worth adding might be to uncomment the line dbscan = 'foo' //= ... and add a ts-expect-error comment, which (I'm pretty sure) would generate a compile time error if that line ever started working.

At least that way we could tell if the property name constraint was ever weakened.

"noEmit": true
},
"files": ["types.ts"]
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

If you wanted to consolidate this further, you can make a root tsconfig.testTypes.json which has all of this and then each package can just be an extends directive. With ${configDir} things like files are now possible to define once. https://devblogs.microsoft.com/typescript/announcing-typescript-5-5/#the-configdir-template-variable-for-configuration-files

Copy link
Member Author

Choose a reason for hiding this comment

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

That would be great! Wasn't able to find a way to avoid these until now. I will refactor.

Copy link
Collaborator

Choose a reason for hiding this comment

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

#2684 is the Typescript upgrade for Turf

Copy link
Member Author

Choose a reason for hiding this comment

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

Shall we get #2684 merged first then, I'll bring that into this PR, and then rejig to use the configDir stuff?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah feel free to merge it whenever you want, I hit the update button. Actually though this brings up a bit of an interesting point, a new typescript version (or relevant package.json change?) should force everything to rebuild/test.

Copy link
Member Author

Choose a reason for hiding this comment

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

If you mean in CI, then yes it did.

"compilerOptions": {
"noEmit": true
},
"files": ["types.ts"]
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm actually a little surprised this doesn't need to declare index.js, I must be missing something about how the paths directive works or something.

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think tsc can type check the JS implementation at all. So for the JS projects I believe it uses types.ts as an entry point, loads up index.d.ts (infers that's what you really mean when asking to import from index.js), and then checks what it can.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Right but shouldn't index.d.ts have to be listed in files in that case?

Copy link
Member Author

Choose a reason for hiding this comment

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

files seems more to be the top level entry points where you want tsc to start compiling. From there it uses whatever the module resolution policy is to find all the dependencies.

Copy link
Collaborator

Choose a reason for hiding this comment

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

https://www.typescriptlang.org/tsconfig/#files I guess it doesn't say that it has to be exhaustive, but I always read that as needing to list every file. 🤷

@@ -0,0 +1 @@
import { lineSliceAlong } from "./index";
Copy link
Collaborator

Choose a reason for hiding this comment

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

Probably needs some usage if this file exists.

Copy link
Member Author

Choose a reason for hiding this comment

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

Unless I'm missing your point, this is probably as good as we can make it, at least for a JS project. We could add example calls to lineSliceAlong, but all that's will confirm is we've written the example calls to match the signatures defined in index.d.ts

Alternative tack: do we not bother doing static type checking in our remaining JS packages? I don't think it's really adding much.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah if we aren't getting benefit from types.ts testing, maybe we can just remove it entirely. Something like https://api-extractor.com/ might be better for making sure we don't actually break APIs than these manual tests.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ok so I dug in on one of the types.ts files and it originally came from here which is part of the early Typescript implementation, so maybe in 2024 they are less relevant.

74140e8

Copy link
Member Author

Choose a reason for hiding this comment

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

So, sounds like we're happy to ditch the types.ts for remaining JS projects, knowing that we'll have actually useful static type checking once they're converted to TS?

API extractor worth investigating too 👍

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd like to get more consensus around this decision, especially since I wasn't around when the decision was made to include them. I don't want to delay this PR indefinitely though.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure thing. I'll proceed as if we will retire them for the remaining JS projects, and if justification comes up to keep them, it's easy to revert before we merge this PR.

Copy link
Member Author

Choose a reason for hiding this comment

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

The above is done. To further simplify configuration (MRL mainly) all packages now run tsc. For the JS projects though it's pretty much a no-op as it can't make any guarantees about the type safety of the JS code.

@@ -0,0 +1 @@
import { lineSlice } from "./index";
Copy link
Collaborator

Choose a reason for hiding this comment

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

Probably needs some usage if this file exists.

"test": "echo '@turf/turf tests run in the last-checks step'"
"build": "tsup --config ../../tsup.config.ts",
"cdnBundle": "npm-run-all cdnBundle:minify cdnBundle:checks",
"cdnBundle:checks": "tsx test.ts && tsx test.example.js && tsx test.ts",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Double tsx test.ts here ^

Copy link
Member Author

Choose a reason for hiding this comment

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

Just being extra cautious. Will fix this.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed

@smallsaucepan
Copy link
Member Author

Thanks for that feedback @mfedderly. Let me know what you think about the type test comments. Everything else I'll refactor in coming days and push up.

…packages the same. Only exception now is @turf/turf.

Run tsc static type checking in all packages (except @turf/turf), even the JS ones. It's a no-op for JS, though standardises our package structure somewhat.
Added a bunch of standard devDependencies to all projects including typescript, tslib, bench, tape, npm-run-all. Same rationale as above.
Upgraded to typescript 5.5.4.
Retired types.ts tests as most of them weren't adding much value any more now that we use tsc --noEmit to type check TS code.
…inting of generated code (escheck). We now run the former during top level test, and the latter only during pre-publish after we've run a top level build.

Upgraded the eslint plugin to match new typescript version, fixed a couple of minor "new" lint errors.
@smallsaucepan smallsaucepan marked this pull request as draft September 28, 2024 14:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants