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

fix(amplify-provider-awscloudformation): custom transformer imports #3236

Merged

Conversation

hirochachacha
Copy link
Contributor

@hirochachacha hirochachacha commented Jan 22, 2020

Current implementation misuses createRequireFromPath API.
Unfortunately this API doesn't support directory path well.

For example,

createRequireFromPath("/my/project")("./node_modules/mylib")

isn't same as

require("/my/project/node_modules/mylib")

Actually, it's

require("/my/node_modules/mylib")

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@codecov-io
Copy link

codecov-io commented Jan 22, 2020

Codecov Report

Merging #3236 into master will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@           Coverage Diff           @@
##           master    aws-amplify/amplify-cli#3236   +/-   ##
=======================================
  Coverage   59.39%   59.39%           
=======================================
  Files         254      254           
  Lines       12590    12590           
  Branches     2637     2637           
=======================================
  Hits         7478     7478           
  Misses       4780     4780           
  Partials      332      332

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 3a5e43a...177f12a. Read the comment docs.

const projectRootPath = context.amplify.pathManager.searchProjectRootPath();
const { createRequireFromPath } = require('module');
const projectRequire = createRequireFromPath(projectRootPath);

if (tempModulePath.startsWith('./')) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

It seems that there is no concept like global require and local require.
module.paths is calculated by caller's path and NODE_PATH.
https://stackoverflow.com/questions/15636367/nodejs-require-a-global-module-package

@hirochachacha
Copy link
Contributor Author

hirochachacha commented Jan 22, 2020

I think this issue is important one. Currently there is no reasonable way to use custom transformers coming from npm package. I realized that after publishing a package.

@attilah
Copy link
Contributor

attilah commented Jan 22, 2020

@hirochachacha could you please elaborate on what is NOT working with the current implementation?

@attilah attilah self-assigned this Jan 22, 2020
@hirochachacha
Copy link
Contributor Author

@attilah I tried to use my custom transformer.

  1. yarn add -D graphql-auto-transformer
  2. edit amplify/backend/api/<YOUR_API>/transform.conf
    "transformers": [
      "./graphql-auto-transformer"
    ]
  1. amplify-dev api gql-compile

Then I saw

Error: Cannot find module 'graphql-auto-transformer'

The following code is a short summary of current implementation.

require('module').createRequireFromPath(__dirname)("./graphql-auto-transformer")

You can check that this line of code cannot load graphql-auto-transformer from node_modules.

And next one is proposed solution.

require('module').createRequireFromPath(`${__dirname}/noop.js`)('graphql-auto-transformer');

@hirochachacha
Copy link
Contributor Author

hirochachacha commented Jan 22, 2020

https://github.com/nodejs/node/blob/dcba12895ad58275ba5b027c2c5110461dfebf66/lib/internal/modules/cjs/loader.js#L1263-L1268

Recent node supports directory path through trailing slash, but stable node 10.x doesn't include this change. So we need to handle it manually.

@hirochachacha
Copy link
Contributor Author

@attilah is it still unclear?

@sbue
Copy link

sbue commented Mar 22, 2020

Any update on this?

@andreialecu
Copy link
Contributor

@attilah it seems that you attempted to fix: aws-amplify/docs#992 (comment), as per: aws-amplify/docs#992 (comment).

However, it doesn't seem to work for locally installed packages.

For clarity here's what happens on amplify-cli 4.18.0:

transform.conf.json

{
  "Version": 5,
  "ElasticsearchWarning": true,
  "transformers": ["graphql-auto-transformer"]
}

amplify push output:

image

There is a workaround here: hirochachacha/graphql-auto-transformer#2

However, that adds additional maintenance complexity and comes with its own set of issues.

@hirochachacha
Copy link
Contributor Author

@attilah I still don't understand why you want to ignore this issue. Maybe you don't think this is an issue because you've already tested custom imports feature? https://github.com/amazon-archives/aws-reinvent-2019-mobile-workshops/tree/master/MOB405
If so, that's pretty wrong.

https://github.com/amazon-archives/aws-reinvent-2019-mobile-workshops/blob/master/MOB405/Starting%20from%20scratch.md#add-validator-transformer-to-list-of-custom-transformers-in-amplifybackendapivalidatortesttransformconfjson

"transformers": [
    "./reinvent-string-validator"
]

You expect that "reinvent-string-validator" module will be loaded from
"<project_dir>/node_modules/reinvent-string-validator" right?

But in fact, it will be loaded from "<project_dir>/../reinvent-string-validator"
That's the reason you didn't see this issue from your tests. The directory structure concealed it.

@andreialecu
Copy link
Contributor

@hirochachacha would there be a way to provide the full path in the config?

For example:

"transformers": [
    "../node_modules/graphql-auto-transformer"
]

My various attempts have been unsuccessful.

@hirochachacha
Copy link
Contributor Author

@andreialecu you mean, except using NODE_PATH?

cd <your_project>/..
ln -s <your_project>/node_modules/graphql-auto-transformer

It emulates directory structure of https://github.com/amazon-archives/aws-reinvent-2019-mobile-workshops/tree/master/MOB405
So it should work.

@hirochachacha
Copy link
Contributor Author

Using absolute path should work also.

@andreialecu
Copy link
Contributor

I got it, the following works without linking:

    "transformers": [
        "./projectname/node_modules/graphql-auto-transformer"
    ]

It's not ideal having to hardcode the root folder name in it though.

@hirochachacha
Copy link
Contributor Author

Yep, I hope these work-arounds prove that this is, obviously, an issue.

@attilah
Copy link
Contributor

attilah commented May 4, 2020

@hirochachacha thanks for the PR and for finding this issue. I tested the proposed solution with your published package and it failed with globally installed packages (when using NVM) and if the package was referenced with ./ prefix in transform.conf.json.

The resolve-from package also uses the same "trick" for noop.js what you applied, I was not aware of that before.

I did some refactoring by leveraging import-global and import-from packages and it worked for me in all the test cases.

@hirochachacha, @andreialecu, @sbue, test my commit if it works for your use cases and to make sure this time we get it right.

@hirochachacha as previously we asked, please open an issue before opening a PR to be able to discuss the given issue and prioritize it better.

Current implementation misuses createRequireFromPath API.
Unfortunately this API doesn't support directory path well.

For example,

```
createRequireFromPath("/my/project")("./node_modules/mylib")
```

isn't same as

```
require("/my/project/node_modules/mylib")
```

Actually, it's

```
require("/my/node_modules/mylib")
```
@hirochachacha
Copy link
Contributor Author

@attilah

@hirochachacha thanks for the PR and for finding this issue. I tested the proposed solution with your published package and it failed with globally installed packages (when using NVM) and if the package was referenced with ./ prefix in transform.conf.json.

That's probably working as intended.
Look, we don't have to distinguish local import and global import. local import covers global one.
You can confirm this by printing module.paths.

[ '/Users/hiroshiioka/work/myproject/node_modules',
  '/Users/hiroshiioka/work/node_modules',
  '/Users/hiroshiioka/node_modules',
  '/Users/node_modules',
  '/node_modules',
  '/Users/hiroshiioka/.node_modules',
  '/Users/hiroshiioka/.node_libraries',
  '/Users/hiroshiioka/.nvm/versions/node/v10.18.1/lib/node' ]

As you can see, it includes both local and global paths.

So, I'd like to change dot import as usual one to support project specific transformer like:

aws-amplify/amplify-category-api#447

Hope it's clear now.

@attilah attilah force-pushed the fix/custom_transformer_imports branch from 3de4c1b to 2053463 Compare May 4, 2020 03:15
@attilah
Copy link
Contributor

attilah commented May 4, 2020

@hirochachacha it is environment dependent, by default you don't get the nvm one, in my setup I don't have it, here is the require.main.paths when the gql-compile running:

[
  "<root>/amplify-cli/packages/amplify-cli/bin/node_modules",
  "<root>/amplify-cli/packages/amplify-cli/node_modules",
  "<root>/amplify-cli/packages/node_modules",
  "<root>/amplify-cli/node_modules",
  "<root>/node_modules",
  "<home>/node_modules",
  "/Users/node_modules",
  "/node_modules",
]

That's the reason we need the additional global import.

@hirochachacha
Copy link
Contributor Author

@attilah I see. That's interesting. I thought you're talking about special handling for './' in the previous code without reading the PR. Sorry about that. Now it looks like it's good although I didn't test it yet. Thank you for the fix.

@attilah
Copy link
Contributor

attilah commented May 4, 2020

@hirochachacha if you encounter a problem just open an issue.

@attilah
Copy link
Contributor

attilah commented May 4, 2020

@yuth please review my changes in this PR.

@hirochachacha
Copy link
Contributor Author

But I just wonder why do you need third party libraries? Even though hirochachacha@e1621a8 failed to load global libraries, pulling back hirochachacha@e1621a8#diff-13054d37b1e2418fc273d5352c4bcd76L105-L108 wasn't good enough?

@hirochachacha
Copy link
Contributor Author

@attilah @yuth Wait, this's still wrong. https://github.com/sindresorhus/import-from doesn't follow node's module paths, it only resolve specific path. Besides, it doesn't distinguish graphql-auto-transformer and ./graphql-auto-transformer, so there're no chance to accomplish aws-amplify/amplify-category-api#447
Again, I'd suggest to stick with createRequireFromPath or its successor createRequire for providing maximum functionalities to users.

@attilah
Copy link
Contributor

attilah commented May 6, 2020

@hirochachacha this change supports:

  • import module/js file by absolute path
  • import package from current project's node_modules (import-from), package name can be specified with './' prefix or without it
  • import package from global packages

What should ./ accomplish in addition to this? if you specify ./ that was meant for current project's node_modules. What am I missing?

Longer term, transformers will be consumable from plugin packages as that is how you can add extensions to the CLI, but that will require changes, but will work similarly how functions category is doing it now.

@attilah attilah merged commit 7794d73 into aws-amplify:master May 6, 2020
@github-actions
Copy link

This pull request has been automatically locked since there hasn't been any recent activity after it was closed. Please open a new issue for related bugs.

Looking for a help forum? We recommend joining the Amplify Community Discord server *-help channels for those types of questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 24, 2021
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.

None yet

6 participants