Skip to content
This repository was archived by the owner on Oct 5, 2021. It is now read-only.

feat: config file#55

Merged
urish merged 17 commits intomockdeep:masterfrom
zoehneto:config-file
Apr 9, 2018
Merged

feat: config file#55
urish merged 17 commits intomockdeep:masterfrom
zoehneto:config-file

Conversation

@zoehneto
Copy link
Contributor

@zoehneto zoehneto commented Apr 1, 2018

Implements #31

  • Implement configuration parsing and validation against schema
  • Implement helpers which generate the config for instrument and applyTypes
  • Add support to typewiz-webpack
  • Add support to typewiz-node

Copy link
Contributor

@MadaraUchiha MadaraUchiha left a comment

Choose a reason for hiding this comment

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

Just a few comments, nothing huge.

import { ConfigurationParser } from './configuration-parser';

describe('ConfigurationParser', () => {
it('should throw an error if given non-existing typewiz.json file', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure that it should explicitly throw an error when given a nonexistent file.

When there's no configuration, we should probably use some sane defaults.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes sense, I'll default to an empty config (since this is allowed as well with a config file specified and the defaults are handled by the code using the config (instrument.ts, apply-types.ts and compiler-helper.ts)).

private typewizConfig: any;

public parse(configurationPath: string) {
const typewizConfigSchema = JSON.parse(fs.readFileSync('src/typewiz.json', { encoding: 'utf8' }));
Copy link
Contributor

Choose a reason for hiding this comment

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

While we do have sync IO in other places, it's generally where it's totally guaranteed that it's in the first tick (i.e. it's a CLI, or it's at the very start of a program running).

There's no such guarantee here, it might be better to use util.promisify() on fs.readFile and async/await.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right now ci runs against node 6.4.0 which doesn't have util.promisify() (async await should be transpiled by typescript). Is support for node 6 important or can we use util.promisify() (and then remove node 6 from ci since it wont work anymore.

Copy link
Contributor

Choose a reason for hiding this comment

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

@urish Thoughts? Node 8.9.0 is the LTS, how far back do we want to support node versions?


let typewizConfigString;
try {
typewizConfigString = fs.readFileSync(path.resolve(configurationPath), { encoding: 'utf8' });
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as the last comment, sync IO is bad in low level APIs :(

@MadaraUchiha
Copy link
Contributor

MadaraUchiha commented Apr 1, 2018

Very nice! I've reviewed the changes so far. Huzzah 🎉

@zoehneto
Copy link
Contributor Author

zoehneto commented Apr 2, 2018

@urish I have made the readFile calls asynchronous using util.promisify which breaks node 6 (see review above). If that is a problem I can add the polyfill, otherwise I would remove node 6 from .travis.yml.

public parse(configurationPath: string) {
const typewizConfigSchema = JSON.parse(fs.readFileSync('src/typewiz.json', { encoding: 'utf8' }));
public async parse(configurationPath: string): Promise<void> {
const readFileAsync = util.promisify(fs.readFile);
Copy link
Contributor

Choose a reason for hiding this comment

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

You should probably do this outside, promisifying is expensive and we shouldn't do it every time parse() is called ;)

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 can store it in the class but that leads to issues getting the type right: in the node types readFile is actually 4 different functions all overloading the same name (because the return type changes based on whether you specify an encoding etc.). Do you know how to write that type without having to cast it when assigning it, using it and also not using any?

Copy link
Contributor

@MadaraUchiha MadaraUchiha Apr 2, 2018

Choose a reason for hiding this comment

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

All I meant was that you move const readFileAsync = ... outside the class, so that the promisification only happens once. There shouldn't be any new typing problems (literally cut and paste line 10 to above the class ... line).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, of course :) I just made the change.

@coveralls
Copy link

coveralls commented Apr 2, 2018

Coverage Status

Coverage increased (+4.005%) to 90.309% when pulling 9bb3152 on zoehneto:config-file into d9b532c on urish:master.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.7%) to 86.957% when pulling abd9aee on zoehneto:config-file into 5b9c3d1 on urish:master.

@zoehneto
Copy link
Contributor Author

zoehneto commented Apr 2, 2018

I added support for the config file to typewiz-webpack, usage:

use: [ 'ts-loader',
    {
        loader: 'typewiz-webpack',
        options: {
            typewizConfig: path.resolve(__dirname, "../typewiz.json")
        }
    }],

This only supports the instrumentation though, since typewiz-webpack doesn't apply types (it creates a json file with all gathered type information.

@urish
Copy link
Collaborator

urish commented Apr 3, 2018

@zoehneto I think we can drop node 6 support from the core library, and then use the polyfill just inside typewiz-node to provide a smoother experience there. WDYT?

@zoehneto
Copy link
Contributor Author

zoehneto commented Apr 3, 2018

According to the docs of the polyfill patching the util module from typewiz-node should work but we definitely have to test that. Do you want to do the same thing with typewiz-webpack?

@zoehneto
Copy link
Contributor Author

zoehneto commented Apr 3, 2018

I just implemented config support for typewiz-node (node 6 polyfill not yet included). It might be necessary to add a function to recursively search for typewiz.json in parent folders to improve mocha handling (since we can't specify a path for mocha) but I can't say since I don't use it and have no way of testing it.

@urish
Copy link
Collaborator

urish commented Apr 3, 2018

@zoehneto migrating to monorepo (#56) shifted some files around - basically, everything that was under src is now in packages/typewiz-core/src. If you need any help with rebasing (now or after the branch is ready), please ping here :-)

@zoehneto
Copy link
Contributor Author

zoehneto commented Apr 4, 2018

@urish I just did the rebase. I also changed the dependency for typewiz-node and typewiz-webpack to typewiz-core instead of typewiz.

…ode 6.4.0 in typewiz-node and typewiz-webpack through a polyfill
@zoehneto
Copy link
Contributor Author

zoehneto commented Apr 4, 2018

@urish I just added the polyfill to typewiz-node and typewiz-webpack and updated the node version requirement for typewiz-core. That means this PR is ready for review, I'll add the documentation once that is done. Please also take a look at the open tasks in the PR description since most of those can only be performed once this PR is merged.

register();
(async () => {
if (process.argv.length > 3) {
await register({ typewizConfig: process.argv[2] });
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 it will make more sense to search for the config file using the same heuristic ts-node uses for finding tsconfig.json, and if we want, we can also allow the user to override this using an optional command line argument (i.e. typewiz-node --typewiz-config filename.json my-awesome-script.ts).

Otherwise, the current behavior can be somewhat confusing - if you run typewiz-node my-awesome-script.ts the script will execute, but then if you try to pass some parameter to the script, e.g. typewiz-node my-awesome-script.ts --kittens, it will treat the script as the configuration file, and you will get an error.

WDYT?

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 think there are two things to consider here:

  • typewiz.json resolution: As mentioned above we could put in a function which recursively searches for typewiz.json in parent directories. I think that certainly makes sense, especially for mocha integration where we can't specify a config file. ts-node uses a typescript function for that which takes the config name as a parameter, so we should be able to use the same logic
  • Argument parsing: Using a proper command line argument parser would certainly make this more robust and nicer to use. The only question for me is how do we want to handle ts-node? Currently you can just parse any ts-node argument to typewiz-node and those arguments will be passed along to ts-node. Do we want to maintain this feature (perhaps by stripping all typewiz config options from process.argv)?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I see the typescript function you mentioned is exported, is it a part of the public API? can we just use it?

Regarding argument parsing, I think that stripping all typewiz config options from process.argv will do the trick. But we can skip this for now, remove the extra argument from typewiz-node and just stick with searching typewiz.json as suggested above, so we can get the PR merged sooner.

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 function is exported from the main entry point for typescript so I assume it is part of the public api.

I just added a configuration finder based on that function, it is now used by typewiz-webpack and typewiz-node. I also removed the optional argument from typewiz-node for now.


public async parse(configurationPath: string): Promise<void> {
const typewizConfigSchema = JSON.parse(
await readFileAsync(path.join(__dirname, 'typewiz.json'), { encoding: 'utf8' }),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Since the schema is not going to change in runtime, why not read it the the beginning of the file using require('./typewiz.json') ? This will also eliminate the need for JSON.parse

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks 👍


let typewizConfigString;
try {
typewizConfigString = await readFileAsync(path.resolve(configurationPath), { encoding: 'utf8' });
Copy link
Collaborator

Choose a reason for hiding this comment

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

How about creating an internal parseConfig() function that will take the config as an object? then we could provide parse() and parseSync() as wrappers, and use parseSync() in typewiz-core/register, so we keep the node registration API sync and simple.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Which file do you mean with typewiz-core/register (I moved typewiz-core/node-register to typewiz-node/node-register since that is the place it is used). In general the way the configuration parser is currently used in typewiz-webpack and typewiz-node it would only make sense to provide a synchronous alternative if it was used for everything (which @MadaraUchiha explicitly didn't want in the first review).

Copy link
Collaborator

Choose a reason for hiding this comment

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

My bad - I meant the register() function exported by typewiz-node. The reason we need that one to be sync is for using with mocha. You use it like:

mocha -r typewiz-node/dist/register src/**/test.ts

If this would be done asynchronously, there is a race condition where mocha might start requiring .ts files before we have registered.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes sense. But that does mean we have to use the synchronous parsing everywhere in typewiz-node (index.ts and node-register.ts).

Copy link
Collaborator

Choose a reason for hiding this comment

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

That sounds okay to me. ts-node and babel both have synchronous register methods, and as far as I can tell, they also read configuration files.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@urish
Copy link
Collaborator

urish commented Apr 8, 2018

Thanks! Two more comments and I think we are ready to go :)

@urish
Copy link
Collaborator

urish commented Apr 9, 2018

Lovely, thank you so much! Merge time :)

@urish urish merged commit d76e5dd into mockdeep:master Apr 9, 2018
@zoehneto
Copy link
Contributor Author

zoehneto commented Apr 9, 2018

Thanks :) What do you want to do about the open tasks still left here (maybe put them in the original issue)?

@urish
Copy link
Collaborator

urish commented Apr 9, 2018

Sounds good! Will you be willing to tackle them as well?

@zoehneto
Copy link
Contributor Author

zoehneto commented Apr 9, 2018

Certainly Schema Store, Documentation and updating the webpack demo (once the new version is relesed). Webpack type applying with config is probably a task to be tackled as part of the cli and correcting the dependency version for typewiz-core would probably best be done by you during publish.

@urish
Copy link
Collaborator

urish commented Apr 9, 2018

Excellent

@zoehneto zoehneto changed the title feat: config file [WIP] feat: config file Apr 9, 2018
@zoehneto
Copy link
Contributor Author

zoehneto commented Apr 9, 2018

I have moved the remaining tasks to issue #31

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants