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

Implement astTransformer - Compatibility with ts-jest 23.10 #204

Merged
merged 6 commits into from
Dec 4, 2018
Merged

Implement astTransformer - Compatibility with ts-jest 23.10 #204

merged 6 commits into from
Dec 4, 2018

Conversation

wtho
Copy link
Collaborator

@wtho wtho commented Nov 28, 2018

Closes #195 and closes #201

Breaking Change

This is a breaking change as Configuration Interface changes (see ts-jest v23.10 docs)

This PR is built upon @huafu's #201, kudos to him for setting up everything and pointing out what was still to be done. Transformer and test are also heavily inspired by his examples in ts-jest. Also see the PR for more information.

Implementation Details

Like the Regex, all the transformer does is transform

  • templateUrl: <PATH> to template: require(<PATH>)
  • styles: <ANYTHING> to styles: []
  • styleUrls <ANYTHING> to styleUrls: []

For more information see the comments in the transformer file or #201.

Edge Cases

I had to make some decisions on how strict the astTransformer is, I decided for this, but I am open to discussion:
Works

import { Component as Cmp } from '@angular/core';
@Cmp({
  templateUrl: './some-path.html'
})
class AngularComp { }

Caveat - this also gets transformed

@SomeOtherDecorator({
  templateUrl: './some-path.html'
})
class SomeClass { }

Does not work

const componentArgs = {
  templateUrl: './some-path.html'
}
@Component(componentArgs)
class AngularComp { }

Additional Notes

  • Run unit tests for the transformer with npm test
  • The transformer was written in TypeScript. A prepare-script for was added, which compiles it to JS before publishing and after npm install.
  • preprocessor.js and its unit tests were removed.

@wtho
Copy link
Collaborator Author

wtho commented Nov 28, 2018

To make the example-app's tests pass again, I would suggest to remove their execution from the circleci tasks for now and add them after this version got published the first time.

After that the tests will run again referencing jest-preset-angular from package.json avoiding version conflicts and Angular can be bumped to v7.

[Edit: removed irrelevant comment about jest config]

@ahnpnl
Copy link
Collaborator

ahnpnl commented Dec 1, 2018

hi @wtho, I tried to check out your branch, ran npm run build and then npm pack then install the .tgz file to example app. Then I ran test for the components inside example app, I observed that simple.component.ts test failed with this error

yarn run v1.12.3
$ jest
 FAIL  src/app/simple/simple.component.spec.ts
  ● Test suite failed to run

    Cannot find module 'simple.component.html' from 'simple.component.ts'

      3 | @Component({
      4 |   selector: 'app-simple',
    > 5 |   templateUrl: 'simple.component.html',
        |   ^
      6 |   styleUrls: ['./simple.component.css']
      7 | })
      8 | export class SimpleComponent implements OnInit {

      at Resolver.resolveModule (node_modules/jest-resolve/build/index.js:221:17)
      at src/app/simple/simple.component.ts:5:3
      at Object.<anonymous> (src/app/simple/simple.component.ts:8:1)

The same error I saw when running this .tgz file against my company's project. Not all components' tests failed but only a few of them.

README.md Outdated Show resolved Hide resolved
@ahnpnl
Copy link
Collaborator

ahnpnl commented Dec 1, 2018

@thymikee are you familiar with ast transformation ? I don't have much experience with it.

@wtho
Copy link
Collaborator Author

wtho commented Dec 1, 2018

You are correct with the test! I did not know paths like templateUrl: 'page.html' were allowed in angular and have to be transformed before putting them to require.

The transformer now adds ./ if the path does not start with ./, ../ or /. The simple-component test should work now.

The prepare Script is a special one that will automatically run after you npm install without any additional args and before npm publish, so it is not required to call npm run build.

To make the example-app tests pass you have to run:

cd example
npm install

# install version of this PR in example-app, will also get [email protected]
# and build InlineHtmlStripStylesTransformer.js from ts
# (did not know about that, but works! npm ftw!)
npm install wtho/jest-preset-angular#ts-jest-23-10

npm test

I wanted to add this workaround to the CI-Tests as well until next publication, but yarn does not build the package after installed from github directly as npm does, so it would have been more complicated.

I has issues with jest's caching, as I ran the tests long ago, so I run the tests with npm test -- --no-cache to make sure the transformer gets called every time.

Also one more test in the example app fails for me, seems to be related to the Serializers (on Linux, Node v11):
AppComponent > snaps:

-       aaa \$1234
+       aaa $1234

-      \`test'chars""
+      `test'chars""

index.js Outdated Show resolved Hide resolved
circle.yml Outdated Show resolved Hide resolved
__tests__/InlineHtmlStripStylesTransformer.test.ts Outdated Show resolved Hide resolved
__tests__/InlineHtmlStripStylesTransformer.test.ts Outdated Show resolved Hide resolved
jest-preset.js Outdated Show resolved Hide resolved
.gitignore Show resolved Hide resolved
__tests__/InlineHtmlStripStylesTransformer.test.ts Outdated Show resolved Hide resolved
const templatePathStringLiteral: StringLiteral = assignment.initializer;
// match if it starts with ./ or ../ or /
if (templatePathStringLiteral.text &&
!templatePathStringLiteral.text.match(/^(\.\/|\.\.\/|\/)/)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I haven't tested this improvement yet but by only reading the codes, it's not so generic to me. Because it's not so sure where the users can put their template, it might be ./ , ../../ or some other places within the project. I think it's better to resolve the path within the project or at least within src/app

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

True!
I just checked the old regex mechanism, it would also not support templateUrl: 'src/page.html' and transform it to ./src/page.html.
When the astTransformer hits, it only gets config-set injected. Luckily, the ConfigSet object seems to have a method resolvePath, I will see later if this serves our purpose on checking if require will be able to find the file later on.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ok I looked into the issue a bit and saw the ts-compiler itself (which is also available in the transformer) offers many more path-tools than expected:
toPath, normalizePath, normalizePathAndParts, getDirectoryPath, isRootedDiskPath, isDiskPathRoot, convertToRelativePath, getPathComponents, reducePathComponents, getNormalizedPathComponents, getNormalizedAbsolutePath, getPathFromPathComponents, getRelativePathFromFile, getRelativePathFromDirectory, getRelativePathToDirectoryOrUrl, ensurePathIsNonModuleName, combinePaths, resolvePath, comparePathsCaseSensitive, comparePathsCaseInsensitive, comparePaths, containsPath, extensionFromPath, tryGetExtensionFromPath, getAnyExtensionFromPath, fullTripleSlashReferencePathRegEx, fullTripleSlashAMDReferencePathRegEx, getExternalModuleNameFromPath, getOwnEmitOutputFilePath, getDeclarationEmitOutputFilePath, getSourceFilePathInNewDir, getOutputPathsFor, updateMissingFilePathsWatch, getDefaultLibFilePath

But it feel like it would be an own task to figure out which methods help us to figure out if the file path is relative to the transformed file or relative to the project root using any path-definition inside tsconfig.json.

I quickly checked resolvePath, but that always appends the file to the project root.

Would you be ok with opening a new issue and for now keep the same behavior as the regex did before?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Of course opening another PR for it is always fine by us👌 The only problem is we will need to create a temporary branch for this PR to merge in instead of master. Because master branch at the moment doesn't encounter this issue therefore we want to keep the state like it is right now. Until we solve the issue with this resolve path we can merge the branch of this PR into master. Do you agree @thymikee ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I disagree, the regex I use, /^(\.\/|\.\.\/|\/)/, does (in combination with moving the string literal and renaming the property assignment to template) the same thing as the regex in master /templateUrl\s*:\s*('|"|)(./){0,}(.*)('|"|)/g, so I see no issue with merging to master for now.

The problem you describe (src/component => ./src/component) has existed before.

I think this is not a reason to hold back this PR.

Copy link
Collaborator

Choose a reason for hiding this comment

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

hmm ok. I hope we make the users happy with this breaking change before they find out this hidden bug :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Quick Intro AST and the transformer

(Ignore if you know this already)

If you play with a ast-viewer by putting in a simple Angular Component you can get familiar with the AST that TSC will parse form the code. It is a big tree of nested nodes.

Every AST node will have a kind-property, which is a SyntaxKind (all of them listed in typescript.d.ts).

For every SyntaxKind there is a is<KIND>(node)-method, e. g. isStringLiteral(node). I use these in the transformer, e. g. here, and can then safely assume the node is of that kind and has the corresponding fields, so there is no need to check against the number 'manually'.

Once called getMutableClone() on a node it is possible to modify inner nodes. Nodes can also be programmatically created, as explained here in the TS docs. We have to do this in the transformer to e. g. create the require()-call.

Being familiar with this enables you to write custom TSLint rules as well.

@wtho
Copy link
Collaborator Author

wtho commented Dec 1, 2018

Thank you for your feedback @thymikee @ahnpnl, feel free to check out the latest changes!

package.json Outdated Show resolved Hide resolved
 * Remove obsolete processor which does not work with ts-jest 23.10
 * Add AstTransformer to work with the new ts-jest API, that replaces
templateUrl with template and a require call and strips off styles from
Components for Tests in Jest
 * Add build commands to repository as transformer is written in
TypeScript
 * Call build in prepare-script to include it in publish
 * ASTTransformer handles non-relative/non-absolute templateUrl by
prefixing them with './'
@wtho
Copy link
Collaborator Author

wtho commented Dec 2, 2018

⚠️ Attention

I rebased on top of the updated master (to include #207) and - as all of the commit hashes in this PR changed anyways - took the opportunity to squash all of my commits into one. huafu's commit remained untouched.

@ahnpnl
Copy link
Collaborator

ahnpnl commented Dec 2, 2018

thanks 👍 Strange CI still failed because one of the snapshots

@wtho
Copy link
Collaborator Author

wtho commented Dec 2, 2018

Strange CI still failed because one of the snapshots

Yeah it's odd, I want to figure this out before merging.

@wtho
Copy link
Collaborator Author

wtho commented Dec 2, 2018

Ok so this is also a breaking change of ts-jest 23.10, of which they are probably not aware of
cc @huafu

Previously ts-jest used to handle html just calling JSON.stringify(htmlFileContent) in preprocess

now they create a module using result = `module.exports=${JSON.stringify(source)}` in ts-jest-transformer and process it like a file using babel.

This changes the output in the snapshots as (I guess) babel removes the escaping.

How do we handle this?

@ahnpnl
Copy link
Collaborator

ahnpnl commented Dec 2, 2018

It comes from stringifyContentPathRegex which to keep backward compat of the old option __HTML_TRANSFORM__. Even though the option itself is to keep the existing test working but potentially breaks some snapshot files.

Btw I like the new snapshot more :) the new snapshot actually is more correct than the old one.

@wtho
Copy link
Collaborator Author

wtho commented Dec 2, 2018

Ok, so summarizing these things are still open:

  • I will update the snapshots that test the escape characters
  • Where will we explain the users the breaking change? There should be a mention of the changes in the config somewhere and a note that the snapshots might have a different outcome. In the README? Or elaborate them more in the Changelog?
  • Can the transpiled file in .gitignore be kept there for now?

Follow-up issues

  • Take Serializers to TS and build them as well
  • Open issue with the bug: templateUrl: 'src/page.html'
  • Another issue (maybe you have encountered it before): When I run (on the example app) yarn test -t='snap$', the test fails as well. The tests in app.component.spec.ts depend on being run in the order of occurrence as in the file. This is not optimal.

@ahnpnl
Copy link
Collaborator

ahnpnl commented Dec 3, 2018

  • Breaking changes should be shortly explained in CHANGLOG and link to this PR. Perhaps in this case of snapshot you can refer it to ts-jest documentation.
  • I saw the comment from @thymikee about transpile file and convert file in this project to ts file. I guess it's ok to leave it in .gitignore.
  • The 3rd issue about the test of app component I am not aware of that error. We have a lot of rooms to improve tests in example app.

@thymikee
Copy link
Owner

thymikee commented Dec 3, 2018

Breaking changes should be shortly explained in CHANGLOG and link to this PR. Perhaps in this case of snapshot you can refer it to ts-jest documentation.

Yup, plus if there's a migration path, feel free to also add it to the changelog :)

@wtho
Copy link
Collaborator Author

wtho commented Dec 4, 2018

I updated the changelog and added a migration guide (ts-jest does apparently not offer one, so I explained the changes to be made in the config).

Btw the snapshots does not differ because of JSON.stringify, but because of the regex ESCAPE_TEMPLATE_REGEX added in #34 is not used anymore. It is not required anymore, as the transformer works on the AST instead of on character-based regexes. I mentioned this in the changelog as well.

Feel free to edit the changelog in any way before the v7 release.

Copy link
Owner

@thymikee thymikee left a comment

Choose a reason for hiding this comment

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

This is awesome! Thank you so much on working on this. It means a lot, especially since I have no time for feature development like this anymore.
Left 2 small nits to address, once they're resolved we're gonna merge it and ship as an alpha for testing.

package.json Outdated Show resolved Hide resolved
package.json Outdated Show resolved Hide resolved
@thymikee
Copy link
Owner

thymikee commented Dec 4, 2018

@wtho let me know if you'd like to help more on the project and become a collaborator just like @ahnpnl who also does tremendous job keeping this project alive.

package.json Outdated Show resolved Hide resolved
@wtho
Copy link
Collaborator Author

wtho commented Dec 4, 2018

@wtho let me know if you'd like to help more on the project and become a collaborator just like @ahnpnl who also does tremendous job keeping this project alive.

Yeah, I would love to! Also thank you both for helping me through this PR so far.
It is great you are so quick with responses and giving constructive feedback, this is how open source is fun!

@thymikee thymikee merged commit adad842 into thymikee:master Dec 4, 2018
@wtho wtho deleted the ts-jest-23-10 branch December 4, 2018 13:14
@thymikee
Copy link
Owner

thymikee commented Dec 4, 2018

Published this as 7.0.0-alpha.1. If anyone is interested in testing this, feel free to install jest-preset-angular@next :)

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.

Not compatible with [email protected]
4 participants