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

[ENHANCEMENT] Add support for --typescript flag to app and addon blueprints #9972

Merged
merged 16 commits into from Oct 5, 2022

Conversation

simonihmig
Copy link
Contributor

@simonihmig simonihmig commented Jul 22, 2022

This adds the --typescript flag to the ember new and ember addon commands, as per the CLI integration section of RFC800.

While the blueprint changes itself were not very difficult, what did cost a lot of time was debugging and identifying problems with regard to Ember CLI itself and the test coverage.

What is special here, and what uncovered (not caused) the mentioned problems, is that the blueprint delegates much of the work to ember-cli-typescript and its own default blueprint, when the flag is present. This is per se not bad, and it is what the RFC actually says. But by calling addAddonToProject(), it forcefully installs all the dependencies, even in tests, regardless of the --skip-npm flag. This is inevitable I think, as otherwise ember-cli-typescript's blueprint couldn't run in tests. But it makes the test naturally quite slow, and it uncovered some unexpected issues unrelated to the actual changes, which I will walk you through in the following...

  1. It seems the project is shared between tests (see below). When initializeAddons() is called, it is supposed to also call discoverAddons() here. But it happens that _didDiscoverAddons is true here, so discoverAddons() return early. The thing is here, that usually in blueprint tests you only deal with the internal addons of ember-cli. But with the new typescript tests, and external addons are actually installed from npm, we now deal with different sets of this.addons depending on the test. In the typescript test, this.addons still would only contain the internal addons (due to the early return), which when trying to run ember-cli-typescript's default blueprint makes this fail, saying that ember-cli-typescript is an unknown addon (as this.addons hasn't been updated). So I think the fix I added here is legit!?
    Own fix replaced by [BUGFIX release] Make sure newly installed addons are discovered when running ember install #10014

  2. When running the typescript blueprint tests (which again install real external dependencies) with npm, an (unrelated) peer dependency issue arises, which is described here: peerDeps don't support Ember beta with npm emberjs/ember-test-helpers#1236. This makes the test fail, but the issue is not related to the test itself, it's real. Therefor for now, the added tests only work with yarn, which does not share this behavior around peer deps.

  3. When running the blueprint acceptance tests, they call await ember(['new', 'foo', '--skip-npm', '--skip-bower', '--skip-git']); or similar. This eventually calls into Project#nullProject(), which return a singleton project instance. But this means this instance is shared (leaked!) between tests. This seems to not have caused issues before, as the instance was not fundamentally different for different tests. But it is now! If you look back at item 1., you see that for the typescript tests real external addons are added to this.addons, which is not the case for all the other tests. But now that this state is leaking between tests, the following happens:
    Any tests would pass when running in isolation. But any of the tests that follows one of the new typescript tests would fail when calling await ember(...), with Cannot find module './commands' thrown when @ember/optional-features tries to (lazily!) require it here. See this CI run. This happens because in the (leaked) project instance this.addons still contains all the external addons in memory (including @ember/optional-features, which just happens to cause the error as it's the first one), while the actual modules on disk in the tmp folder have been deleted by the (previous) test's afterEach. So that require naturally fails. But the root issue here is that the project's state is leaking here between tests I think.
    I was able to overcome this with a quick'n'dirty hack in the last commit, which basically removes the singleton behavior. In the latest test run you can see that all of the blueprint tests are passing now! But...

Fixed, see #9972 (comment).

  1. CI is now failing with a JavaScript heap out of memory error. Probably this is due to this hack, which might be causing some memory leaks? However I didn't want to spend even more time on investigating this, before the real path to overcoming 3. is not determined.

So 3. is really the point where I would like some assistance from some CLI experts here on how to proceed with that, as I don't fully understand what the deal is with this "null project", and what implications it would have to not treat it as a singleton to prevent it from leaking between tests, or if some other approach is better suited. I'll keep this in a draft state for now, but please review nevertheless!

},
rules: {},
overrides: [
// ts 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.

Putting the TS extends into an override for only .ts files is not something the RFC explicitly mandated, but it is what I have been doing everywhere. Otherwise, when putting this into the global extends, you would get false positives from TS rules when ESLint runs on plain .js files.

If you suggest a different solution, please tell me!

@@ -311,7 +311,7 @@ class Project {
if (fs.existsSync(`${targetsPath}.js`)) {
this._targets = this.require(targetsPath);
} else {
this._targets = require('../../blueprints/app/files/config/targets');
this._targets = { browsers: ['last 1 Chrome versions', 'last 1 Firefox versions', 'last 1 Safari versions'] };
Copy link
Contributor Author

Choose a reason for hiding this comment

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

When requiring ../../blueprints/app/files/config/targets, node would for some reason also try to read from ../../blueprints/app/files/package.json, which is a template file, not a real one.

Previously this caused no issues, as the only parts with "template tags" (or whatever you call these <% %>) was within .name here. But now we have more conditionals in place, which make this package.json template invalid JSON (in the raw format), so node would fail parsing it.

As targets.js is a JS file, I also couldn't just simply read it without making node interpret it (like with something like fs.readJSONSync()), so the only fix I could come up with is to unfortunately just duplicate the exported value here. There is a test covering the output of this, so hopefully the chance of this and the actual targets file diverging is very low! 🤔

lib/models/project.js Outdated Show resolved Hide resolved
// node files
<% if (typescript) { %> // ts files
{
files: ['**/*.ts'],
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe this has been changed/fixed in more recent versions of ESLint, but the last I checked, a glob like this would still require you to specify --ext .js,.ts when executing the linter. I've found just '*.ts' still enables linting for nested files while also being simple enough for ESLint to understand it should automatically lint .ts files as well.

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 have this config in a few addons/projects without any problems! 🤷‍♂️

@@ -6,4 +6,4 @@ export default class Router extends EmberRouter {
rootURL = config.rootURL;
}

Router.map(function () {});
Router.map(function () {});<% if (typescript) { %> // eslint-disable-line @typescript-eslint/no-empty-function<% } %>
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 surprised no-empty-function is part of the @typescript-eslint recommended ruleset but not the regular eslint:recommended one. (But that's neither here nor there 😄)

Would changing this to something like the snippet below appease the rule without us needing to explicitly disable it?

Router.map(function () {
  // Add route declarations here
});

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 changing this to something like the snippet below appease the rule without us needing to explicitly disable it?

Yes, it would!

I just wanted to try out what happens with the comment when you add a route by ember g route, only to find out that it actually does not work at all with a router.ts file! Is this known?

Not sure if https://github.com/ember-cli/ember-router-generator would actually support parsing TS, but the error is thrown even before that lib comes into play, in the ember-source blueprint here, as it only checks for a router.js.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yep, that's a known issue (Which we should definitely fix now that TS support is official! Though obviously not a hard blocker for this PR in particular)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now using the suggested comment in the (otherwise still empty) function body!

blueprints/app/files/package.json Outdated Show resolved Hide resolved
@simonihmig
Copy link
Contributor Author

simonihmig commented Aug 18, 2022

For the record: it seems @ef4 has fixed the main nullProject problem here, so thanks a bunch for that! 🎉

For my own record: I recently used this branch to create a real new project, and realized that we need to also...

@ef4
Copy link
Contributor

ef4 commented Aug 18, 2022

Yes, the NullProject question is resolved. The summary is that NullProject is supposed to be for the case where ember-cli is running outside of any project. That is the situation when you run ember new. But then to install addons into the newly generated project, we need to have a real Project and not NullProject.


function setupApplicationTest(
hooks: Parameters<typeof upstreamSetupApplicationTest>[0],
options?: Parameters<typeof upstreamSetupApplicationTest>[1]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

TS team: please have a look here!

I could have typed the first arg as hooks: NestedHooks, as @types/ember-qunit declares this type globally. But not the SetupOptions interface, which is not even exported. So the only way I found to type this was like this. And for consistency I chose to do this for both args...

Copy link
Contributor

Choose a reason for hiding this comment

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

This could also be (...[hooks, options]: Parameters<typeof upstreamSetupApplicationTest>), not that that's much better. I think we should probably just export the SetupOptions type from @types/ember-qunit.

Copy link
Contributor

Choose a reason for hiding this comment

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

I am doing that now!

Copy link
Contributor

Choose a reason for hiding this comment

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

DefinitelyTyped/DefinitelyTyped#62073 – I exported it as @internal, since it is expressly not part of the public API, but is important for collaborators here. I also opened emberjs/ember-qunit#957 to track converting ember-qunit to TS!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Using the new export now!

@simonihmig simonihmig force-pushed the ts-blueprint branch 2 times, most recently from f967b7d to 25aa489 Compare August 18, 2022 18:58
@simonihmig
Copy link
Contributor Author

I think this PR is good now, with only the somewhat philosophical question remaining of how to name and integrate the type checking script.

So please give this a/another review, Ember-CLI team & TS-Team (@dfreeman @chriskrycho @jamescdavis)!

@simonihmig simonihmig marked this pull request as ready for review August 19, 2022 07:07
@simonihmig simonihmig changed the title Add support for --typescript in app/addon blueprint [ENHANCEMENT] Add support for --typescript in app/addon blueprint Aug 19, 2022
@ef4
Copy link
Contributor

ef4 commented Aug 19, 2022

(Please don't take my discussion above over the definition of "lint" as a blocker, I've said my piece but I can live with whatever will get this moving.)

@chriskrycho
Copy link
Contributor

chriskrycho commented Aug 19, 2022

Will review Monday or Tuesday. And for what it's worth the Typed Ember team generally shared the "not going to block on this" POV about naming. It's probably unsurprising that we do disagree about whether to think about type checking and linting as equivalent—but we would all rather ship this with a name we don't love than not ship it while arguing over that name!

chriskrycho added a commit to chriskrycho/DefinitelyTyped that referenced this pull request Sep 1, 2022
chriskrycho added a commit to chriskrycho/DefinitelyTyped that referenced this pull request Sep 1, 2022
Copy link
Contributor

@chriskrycho chriskrycho left a comment

Choose a reason for hiding this comment

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

All right, one suggested tweak here, but looks good otherwise!

blueprints/app/files/package.json Outdated Show resolved Hide resolved
typescript-bot pushed a commit to DefinitelyTyped/DefinitelyTyped that referenced this pull request Sep 1, 2022


* ember-qunit: make SetupTestOptions public

This unblocks ember-cli/ember-cli#9972.

* ember-qunit: document that SetupTestOptions is internal

* ember-qunit: SetupTestOptions is *not* internal!

* ember-qunit: only change ember-qunit, not other Ember stuff
@simonihmig
Copy link
Contributor Author

Status: Will wait for #10014 to get merged and rebase, after that this PR should hopefully be good then!

Copy link
Contributor

@bertdeblock bertdeblock left a comment

Choose a reason for hiding this comment

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

Noticed a few things:

  1. When generating an app or addon, I'm seeing the following warning logged about 40 times:
You passed the '--typescript' flag but there is no TypeScript blueprint available. A JavaScript blueprint will be generated instead.
  1. Running ember s in a new app or addon fails with the following error:
Error: Typechecking failed
  1. Do we want/need to specify/control the version of ember-cli-typescript?

tests/acceptance/new-test.js Outdated Show resolved Hide resolved
@simonihmig
Copy link
Contributor Author

When generating an app or addon, I'm seeing the following warning logged about 40 times:
You passed the '--typescript' flag but there is no TypeScript blueprint available. A JavaScript blueprint will be generated instead.

The logic around this warning message would emit the warning whenever the --typescript flag is explicitly passed, and the blueprint files contain any non-ts files, like hbs. So this seems to be a general bug, that probably way only uncovered now with the blueprint shipping so many other non-ts files. Fixed that in 538acd0.

Running ember s in a new app or addon fails with the following error:
Error: Typechecking failed

I realized that some types packages like @types/ember-qunit were not installed by e-c-ts' own blueprint, as the fix by @ef4 would leave the stale version of project in blueprintOptions, so the e-c-ts blueprint would not see that ember-qunit is installed here and therefore not install its types. Fixed in 005db39.

In my tests, a generated TS app lints, tests and runs fine now!

Do we want/need to specify/control the version of ember-cli-typescript?

Idk, why would we not want to have the latest version?

@simonihmig
Copy link
Contributor Author

I rebased this, so I think this is good to go now!

@bertdeblock bertdeblock changed the title [ENHANCEMENT] Add support for --typescript in app/addon blueprint [ENHANCEMENT] Add support for --typescript flag to app and addon blueprints Oct 4, 2022
@ef4 ef4 merged commit dea31e7 into ember-cli:master Oct 5, 2022
@ef4
Copy link
Contributor

ef4 commented Oct 5, 2022

Thanks so much, great to have this landing!

@bertdeblock
Copy link
Contributor

Thanks @simonihmig!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants