Release notes v0.0.158
This release shifts the development experience for TS language server users (and sort of forces others to become one). As far as I know, this (#1055) has been the longest open PR that got merged, and even has a predecessor, #461, back in November 2020. So let's get started.
Cli
The Compas cli got a new command: yarn compas init --jscconfig
. It (over)writes the jsconfig.json
in the project directory. This file will become stricter over time, so keep it updated with Compas releases. This command and file is not necessary when only using the frontend code generators, because they imply an existing Typescript setup. To control the Typescript version used, you should add 'typescript' as a 'devDependency' in you project.
Lint config
Since we are adding Typescript definition files to the project, disable automatic fixing of ESLint in your IDE for .ts
and .d.ts
files. This can be skipped if your ESLint config supports TypeScript files, the @compas/lint-config
ones don't.
Code gen
Types
The first change in the code generators is that we now always create a structure.js
file with the used generator options. This is used is 'multi-generate' projects to combine all types in to a single types.d.ts
file to avoid type conflicts down the road.
In a single generate project, e.g. only a single app.generate
call, only a single addition is necessary. Add the following snippet after your app.generate
:
await app.generateTypes({
outputDirectory: "./types/generated",
inputPaths: [],
dumpCompasTypes: true,
});
The dumpCompasTypes
option tells Compas to create a type definition file that makes all major exported types from Compas global in the project. By doing it this way you don't have to import types like App
like
/**
* @typedef {import("@compas/stdlib").App} App
*/
in your project.
In a multi generate project things are a bit more complicated. We have to prevent type conflicts of global types. To ensure this we use the dumpStructure
option of app.generate
and use app.generateTypes
to combine all 'structures' in to a single type definition file.
- Explicitly mention the
enabledGenerators
in eachapp.generate
call, but don't add thetype
generator. Whentype
is the only generator,enabledGenerators
may be an empty array. - Add
dupmStructure: true
to eachapp.generate
call. - Add the above
app.generateTypes
call, but ininputPaths
specify eachoutputDirectory
of yourapp.generate
calls.
This should give you a single type definition file specifying all necessary types.
Validators
Another generator that got a big change is the validator generator. It now collects as many errors as possible before returning an { error }
or { value }
object. This is combined with a few breaking changes:
- No TypeScript file support anymore, so the
app.generate
call will throw if used in combination withuseTypescript: true
. - The
throwingValidator
option is removed, as validators will never throw but instead return an{ error: AppError }
object if any validation error occurred. - The top-level error key is always
validator.error
. All other validation keys are now nested in the 'info' object, with keys representing the 'property path' and values containing the specific validation error and any information. A full example looks something like:- Type definition:
const T = new TypeCreator(); const type = { foo: T.bool(), bar: T.anyOf().values({ type: "car" }, { type: "bicycle" }), };
- Input:
{ "bar": { "type": "scooter" } }
- Resulting error:
{ "key": "validator.error", "info": { "$.foo": { "key": "validation.boolean.undefined", "info": {} }, "$.bar": { "key": "validator.anyOf" }, "$.bar.type": [ { "key": "validator.string.oneOf", "info": { "oneOf": ["car"] } }, { "key": "validator.string.oneOf", "info": { "oneOf": ["bicycle"] } } ] } }
Note that for anyOf
validators the result may contain an array, since the property path may be the same.
In closing
There is a bunch of improvements & automation to be added, like auto generating documentation from the generated type definitions, writing tests for the type definitions and making the public api surface more strict by adding @private
tags. But that's stuff for the future, for now I am back focussing on feature stability and some improvements & additions to existing features.
As always please report missing type definitions and report bugs, thanks!